...

Source file src/github.com/opencontainers/runc/libcontainer/user/user_test.go

Documentation: github.com/opencontainers/runc/libcontainer/user

     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  		// Regression tests for #695.
   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  		// Test for #3036.
   223  		{
   224  			ref: "7456",
   225  			expected: ExecUser{
   226  				Uid:   7456,
   227  				Gid:   100,
   228  				Sgids: []int{1234, 1000}, // 1000 is largegroup GID
   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  		// No such user/group.
   271  		"notuser",
   272  		"notuser:notgroup",
   273  		"root:notgroup",
   274  		"notuser:adm",
   275  		"8888:notgroup",
   276  		"notuser:8888",
   277  
   278  		// Invalid user/group values.
   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  			// empty group
   414  			groups:   []string{},
   415  			expected: []int{},
   416  		},
   417  		{
   418  			// single group
   419  			groups:   []string{"adm"},
   420  			expected: []int{43},
   421  		},
   422  		{
   423  			// multiple groups
   424  			groups:   []string{"adm", "grp"},
   425  			expected: []int{43, 1234},
   426  		},
   427  		{
   428  			// invalid group
   429  			groups:   []string{"adm", "grp", "not-exist"},
   430  			expected: nil,
   431  			hasError: true,
   432  		},
   433  		{
   434  			// group with numeric id
   435  			groups:   []string{"43"},
   436  			expected: []int{43},
   437  		},
   438  		{
   439  			// group with unknown numeric id
   440  			groups:   []string{"adm", "10001"},
   441  			expected: []int{43, 10001},
   442  		},
   443  		{
   444  			// groups specified twice with numeric and name
   445  			groups:   []string{"adm", "43"},
   446  			expected: []int{43},
   447  		},
   448  		{
   449  			// groups with too small id
   450  			groups:   []string{"-1"},
   451  			expected: nil,
   452  			hasError: true,
   453  		},
   454  		{
   455  			// groups with too large id
   456  			groups:   []string{strconv.FormatInt(1<<31, 10)},
   457  			expected: nil,
   458  			hasError: true,
   459  		},
   460  		{
   461  			// group with very long list of users
   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  			// numeric groups only
   494  			groups:   []string{"1234", "5678"},
   495  			expected: []int{1234, 5678},
   496  		},
   497  		{
   498  			// numeric and alphabetic
   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  // Generate a proper "largegroup" entry for group tests.
   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