github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmd/authn/unit_test.go (about)

     1  // Package authn is authentication server for AIStore.
     2  /*
     3   * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package main
     6  
     7  import (
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/NVIDIA/aistore/api/authn"
    12  	"github.com/NVIDIA/aistore/cmd/authn/tok"
    13  	"github.com/NVIDIA/aistore/cmn"
    14  	"github.com/NVIDIA/aistore/cmn/cos"
    15  	"github.com/NVIDIA/aistore/core/mock"
    16  	"github.com/NVIDIA/aistore/tools/tassert"
    17  )
    18  
    19  var (
    20  	users = []string{"user1", "user2", "user3"}
    21  	passs = []string{"pass2", "pass1", "passs"}
    22  )
    23  
    24  func init() {
    25  	// Set default expiration time to 30 minutes
    26  	if Conf.Server.ExpirePeriod == 0 {
    27  		Conf.Server.ExpirePeriod = cos.Duration(time.Minute * 30)
    28  	}
    29  }
    30  
    31  func createUsers(mgr *mgr, t *testing.T) {
    32  	for idx := range users {
    33  		user := &authn.User{ID: users[idx], Password: passs[idx], Roles: []string{GuestRole}}
    34  		err := mgr.addUser(user)
    35  		if err != nil {
    36  			t.Errorf("Failed to create a user %s: %v", users[idx], err)
    37  		}
    38  	}
    39  
    40  	srvUsers, err := mgr.userList()
    41  	tassert.CheckFatal(t, err)
    42  	if len(srvUsers) != len(users)+1 {
    43  		t.Errorf("User count mismatch. Found %d users instead of %d", len(srvUsers), len(users)+1)
    44  	}
    45  	for _, username := range users {
    46  		_, ok := srvUsers[username]
    47  		if !ok {
    48  			t.Errorf("User %q not found", username)
    49  		}
    50  	}
    51  }
    52  
    53  func deleteUsers(mgr *mgr, skipNotExist bool, t *testing.T) {
    54  	var err error
    55  	for _, username := range users {
    56  		err = mgr.delUser(username)
    57  		if err != nil {
    58  			if !cos.IsErrNotFound(err) || !skipNotExist {
    59  				t.Errorf("Failed to delete user %s: %v", username, err)
    60  			}
    61  		}
    62  	}
    63  }
    64  
    65  func testInvalidUser(mgr *mgr, t *testing.T) {
    66  	user := &authn.User{ID: users[0], Password: passs[1], Roles: []string{GuestRole}}
    67  	err := mgr.addUser(user)
    68  	if err == nil {
    69  		t.Errorf("User with the existing name %s was created: %v", users[0], err)
    70  	}
    71  
    72  	nonexisting := "someuser"
    73  	err = mgr.delUser(nonexisting)
    74  	if err == nil {
    75  		t.Errorf("Non-existing user %s was deleted: %v", nonexisting, err)
    76  	}
    77  }
    78  
    79  func testUserDelete(mgr *mgr, t *testing.T) {
    80  	const (
    81  		username = "newuser"
    82  		userpass = "newpass"
    83  	)
    84  	user := &authn.User{ID: username, Password: userpass, Roles: []string{GuestRole}}
    85  	err := mgr.addUser(user)
    86  	if err != nil {
    87  		t.Errorf("Failed to create a user %s: %v", username, err)
    88  	}
    89  	srvUsers, err := mgr.userList()
    90  	tassert.CheckFatal(t, err)
    91  	if len(srvUsers) != len(users)+2 {
    92  		t.Errorf("Expected %d users but found %d", len(users)+2, len(srvUsers))
    93  	}
    94  
    95  	clu := authn.CluACL{
    96  		ID:    "ABCD",
    97  		Alias: "cluster-test",
    98  		URLs:  []string{"http://localhost:8080"},
    99  	}
   100  	if err := mgr.db.Set(clustersCollection, clu.ID, clu); err != nil {
   101  		t.Error(err)
   102  	}
   103  	defer mgr.delCluster(clu.ID)
   104  
   105  	loginMsg := &authn.LoginMsg{ClusterID: clu.Alias}
   106  	token, err := mgr.issueToken(username, userpass, loginMsg)
   107  	if err != nil || token == "" {
   108  		t.Errorf("Failed to generate token for %s: %v", username, err)
   109  	}
   110  
   111  	err = mgr.delUser(username)
   112  	if err != nil {
   113  		t.Errorf("Failed to delete user %s: %v", username, err)
   114  	}
   115  	srvUsers, err = mgr.userList()
   116  	tassert.CheckFatal(t, err)
   117  	if len(srvUsers) != len(users)+1 {
   118  		t.Errorf("Expected %d users but found %d", len(users)+1, len(srvUsers))
   119  	}
   120  	token, err = mgr.issueToken(username, userpass, loginMsg)
   121  	if err == nil {
   122  		t.Errorf("Token issued for deleted user  %s: %v", username, token)
   123  	} else if err != errInvalidCredentials {
   124  		t.Errorf("Invalid error: %v", err)
   125  	}
   126  }
   127  
   128  func TestManager(t *testing.T) {
   129  	driver := mock.NewDBDriver()
   130  	// NOTE: new manager initailizes users DB and adds a default user as a Guest
   131  	mgr, err := newMgr(driver)
   132  	tassert.CheckError(t, err)
   133  	createUsers(mgr, t)
   134  	testInvalidUser(mgr, t)
   135  	testUserDelete(mgr, t)
   136  	deleteUsers(mgr, false, t)
   137  }
   138  
   139  func TestToken(t *testing.T) {
   140  	if testing.Short() {
   141  		t.Skipf("skipping %s in short mode", t.Name())
   142  	}
   143  	var (
   144  		err    error
   145  		token  string
   146  		secret = Conf.Server.Secret
   147  	)
   148  
   149  	driver := mock.NewDBDriver()
   150  	mgr, err := newMgr(driver)
   151  	tassert.CheckFatal(t, err)
   152  	createUsers(mgr, t)
   153  	defer deleteUsers(mgr, false, t)
   154  
   155  	clu := authn.CluACL{
   156  		ID:    "ABCD",
   157  		Alias: "cluster-test",
   158  		URLs:  []string{"http://localhost:8080"},
   159  	}
   160  	if err := mgr.db.Set(clustersCollection, clu.ID, clu); err != nil {
   161  		t.Error(err)
   162  	}
   163  	defer mgr.delCluster(clu.ID)
   164  
   165  	// correct user creds
   166  	shortExpiration := 2 * time.Second
   167  	loginMsg := &authn.LoginMsg{ClusterID: clu.Alias, ExpiresIn: &shortExpiration}
   168  	token, err = mgr.issueToken(users[1], passs[1], loginMsg)
   169  	if err != nil || token == "" {
   170  		t.Errorf("Failed to generate token for %s: %v", users[1], err)
   171  	}
   172  	info, err := tok.DecryptToken(token, secret)
   173  	if err != nil {
   174  		t.Fatalf("Failed to decript token %v: %v", token, err)
   175  	}
   176  	if info.UserID != users[1] {
   177  		t.Errorf("Invalid user %s returned for token of %s", info.UserID, users[1])
   178  	}
   179  
   180  	// incorrect user creds
   181  	loginMsg = &authn.LoginMsg{}
   182  	tokenInval, err := mgr.issueToken(users[1], passs[0], loginMsg)
   183  	if tokenInval != "" || err == nil {
   184  		t.Errorf("Some token generated for incorrect user creds: %v", tokenInval)
   185  	}
   186  
   187  	// expired token test
   188  	time.Sleep(shortExpiration)
   189  	tk, err := tok.DecryptToken(token, secret)
   190  	tassert.CheckFatal(t, err)
   191  	if tk.Expires.After(time.Now()) {
   192  		t.Fatalf("Token must be expired: %s", token)
   193  	}
   194  }
   195  
   196  func TestMergeCluACLS(t *testing.T) {
   197  	tests := []struct {
   198  		title    string
   199  		cluFlt   string
   200  		toACLs   cluACLList
   201  		fromACLs cluACLList
   202  		resACLs  cluACLList
   203  	}{
   204  		{
   205  			title: "The same lists",
   206  			toACLs: []*authn.CluACL{
   207  				{
   208  					ID:     "1234",
   209  					Alias:  "one",
   210  					Access: 20,
   211  				},
   212  				{
   213  					ID:     "5678",
   214  					Alias:  "second",
   215  					Access: 20,
   216  				},
   217  			},
   218  			fromACLs: []*authn.CluACL{
   219  				{
   220  					ID:     "1234",
   221  					Alias:  "one",
   222  					Access: 20,
   223  				},
   224  			},
   225  			resACLs: []*authn.CluACL{
   226  				{
   227  					ID:     "1234",
   228  					Alias:  "one",
   229  					Access: 20,
   230  				},
   231  				{
   232  					ID:     "5678",
   233  					Alias:  "second",
   234  					Access: 20,
   235  				},
   236  			},
   237  		},
   238  		{
   239  			title: "Update permissions only",
   240  			toACLs: []*authn.CluACL{
   241  				{
   242  					ID:     "1234",
   243  					Alias:  "one",
   244  					Access: 20,
   245  				},
   246  				{
   247  					ID:     "5678",
   248  					Alias:  "second",
   249  					Access: 20,
   250  				},
   251  			},
   252  			fromACLs: []*authn.CluACL{
   253  				{
   254  					ID:     "1234",
   255  					Alias:  "one",
   256  					Access: 40,
   257  				},
   258  			},
   259  			resACLs: []*authn.CluACL{
   260  				{
   261  					ID:     "1234",
   262  					Alias:  "one",
   263  					Access: 40,
   264  				},
   265  				{
   266  					ID:     "5678",
   267  					Alias:  "second",
   268  					Access: 20,
   269  				},
   270  			},
   271  		},
   272  		{
   273  			title: "Append new cluster",
   274  			toACLs: []*authn.CluACL{
   275  				{
   276  					ID:     "1234",
   277  					Alias:  "one",
   278  					Access: 20,
   279  				},
   280  				{
   281  					ID:     "5678",
   282  					Alias:  "second",
   283  					Access: 20,
   284  				},
   285  			},
   286  			fromACLs: []*authn.CluACL{
   287  				{
   288  					ID:     "abcde",
   289  					Alias:  "third",
   290  					Access: 40,
   291  				},
   292  				{
   293  					ID:     "hijk",
   294  					Alias:  "fourth",
   295  					Access: 40,
   296  				},
   297  			},
   298  			resACLs: []*authn.CluACL{
   299  				{
   300  					ID:     "1234",
   301  					Alias:  "one",
   302  					Access: 20,
   303  				},
   304  				{
   305  					ID:     "5678",
   306  					Alias:  "second",
   307  					Access: 20,
   308  				},
   309  				{
   310  					ID:     "abcde",
   311  					Alias:  "third",
   312  					Access: 40,
   313  				},
   314  				{
   315  					ID:     "hijk",
   316  					Alias:  "fourth",
   317  					Access: 40,
   318  				},
   319  			},
   320  		},
   321  		{
   322  			title: "Update permissions for existing cluster and apend new ones",
   323  			toACLs: []*authn.CluACL{
   324  				{
   325  					ID:     "1234",
   326  					Alias:  "one",
   327  					Access: 20,
   328  				},
   329  				{
   330  					ID:     "5678",
   331  					Alias:  "second",
   332  					Access: 20,
   333  				},
   334  			},
   335  			fromACLs: []*authn.CluACL{
   336  				{
   337  					ID:     "1234",
   338  					Alias:  "one",
   339  					Access: 40,
   340  				},
   341  				{
   342  					ID:     "abcde",
   343  					Alias:  "third",
   344  					Access: 60,
   345  				},
   346  			},
   347  			resACLs: []*authn.CluACL{
   348  				{
   349  					ID:     "1234",
   350  					Alias:  "one",
   351  					Access: 40,
   352  				},
   353  				{
   354  					ID:     "5678",
   355  					Alias:  "second",
   356  					Access: 20,
   357  				},
   358  				{
   359  					ID:     "abcde",
   360  					Alias:  "third",
   361  					Access: 60,
   362  				},
   363  			},
   364  		},
   365  		{
   366  			title:  "Append only 'abcde' cluster",
   367  			cluFlt: "abcde",
   368  			toACLs: []*authn.CluACL{
   369  				{
   370  					ID:     "1234",
   371  					Alias:  "one",
   372  					Access: 20,
   373  				},
   374  				{
   375  					ID:     "5678",
   376  					Alias:  "second",
   377  					Access: 20,
   378  				},
   379  			},
   380  			fromACLs: []*authn.CluACL{
   381  				{
   382  					ID:     "abcde",
   383  					Alias:  "third",
   384  					Access: 40,
   385  				},
   386  				{
   387  					ID:     "hijk",
   388  					Alias:  "fourth",
   389  					Access: 40,
   390  				},
   391  			},
   392  			resACLs: []*authn.CluACL{
   393  				{
   394  					ID:     "1234",
   395  					Alias:  "one",
   396  					Access: 20,
   397  				},
   398  				{
   399  					ID:     "5678",
   400  					Alias:  "second",
   401  					Access: 20,
   402  				},
   403  				{
   404  					ID:     "abcde",
   405  					Alias:  "third",
   406  					Access: 40,
   407  				},
   408  			},
   409  		},
   410  	}
   411  	for _, test := range tests {
   412  		res := mergeClusterACLs(test.toACLs, test.fromACLs, test.cluFlt)
   413  		for i, r := range res {
   414  			if r.String() != test.resACLs[i].String() || r.Access != test.resACLs[i].Access {
   415  				t.Errorf("%s[filter: %s]: %v[%v] != %v[%v]", test.title, test.cluFlt, r, r.Access, test.resACLs[i], test.resACLs[i].Access)
   416  			}
   417  		}
   418  	}
   419  }
   420  
   421  func newBck(name, provider, uuid string) cmn.Bck {
   422  	return cmn.Bck{
   423  		Name:     name,
   424  		Provider: provider,
   425  		Ns:       cmn.Ns{UUID: uuid},
   426  	}
   427  }
   428  
   429  func TestMergeBckACLS(t *testing.T) {
   430  	tests := []struct {
   431  		title    string
   432  		cluFlt   string
   433  		toACLs   bckACLList
   434  		fromACLs bckACLList
   435  		resACLs  bckACLList
   436  	}{
   437  		{
   438  			title: "Nothing to update",
   439  			toACLs: []*authn.BckACL{
   440  				{
   441  					Bck:    newBck("bck", "ais", "1234"),
   442  					Access: 20,
   443  				},
   444  				{
   445  					Bck:    newBck("bck", "ais", "5678"),
   446  					Access: 20,
   447  				},
   448  			},
   449  			fromACLs: []*authn.BckACL{
   450  				{
   451  					Bck:    newBck("bck", "ais", "1234"),
   452  					Access: 20,
   453  				},
   454  			},
   455  			resACLs: []*authn.BckACL{
   456  				{
   457  					Bck:    newBck("bck", "ais", "1234"),
   458  					Access: 20,
   459  				},
   460  				{
   461  					Bck:    newBck("bck", "ais", "5678"),
   462  					Access: 20,
   463  				},
   464  			},
   465  		},
   466  		{
   467  			title: "Update permissions only",
   468  			toACLs: []*authn.BckACL{
   469  				{
   470  					Bck:    newBck("bck", "ais", "1234"),
   471  					Access: 20,
   472  				},
   473  				{
   474  					Bck:    newBck("bck", "ais", "5678"),
   475  					Access: 20,
   476  				},
   477  			},
   478  			fromACLs: []*authn.BckACL{
   479  				{
   480  					Bck:    newBck("bck", "ais", "5678"),
   481  					Access: 40,
   482  				},
   483  			},
   484  			resACLs: []*authn.BckACL{
   485  				{
   486  					Bck:    newBck("bck", "ais", "1234"),
   487  					Access: 20,
   488  				},
   489  				{
   490  					Bck:    newBck("bck", "ais", "5678"),
   491  					Access: 40,
   492  				},
   493  			},
   494  		},
   495  		{
   496  			title: "Append new buckets",
   497  			toACLs: []*authn.BckACL{
   498  				{
   499  					Bck:    newBck("bck", "ais", "1234"),
   500  					Access: 20,
   501  				},
   502  				{
   503  					Bck:    newBck("bck", "ais", "5678"),
   504  					Access: 20,
   505  				},
   506  			},
   507  			fromACLs: []*authn.BckACL{
   508  				{
   509  					Bck:    newBck("bck", "ais", "5678"),
   510  					Access: 30,
   511  				},
   512  				{
   513  					Bck:    newBck("bck", "aws", "5678"),
   514  					Access: 40,
   515  				},
   516  				{
   517  					Bck:    newBck("bck1", "ais", "1234"),
   518  					Access: 50,
   519  				},
   520  			},
   521  			resACLs: []*authn.BckACL{
   522  				{
   523  					Bck:    newBck("bck", "ais", "1234"),
   524  					Access: 20,
   525  				},
   526  				{
   527  					Bck:    newBck("bck", "ais", "5678"),
   528  					Access: 30,
   529  				},
   530  				{
   531  					Bck:    newBck("bck", "aws", "5678"),
   532  					Access: 40,
   533  				},
   534  				{
   535  					Bck:    newBck("bck1", "ais", "1234"),
   536  					Access: 50,
   537  				},
   538  			},
   539  		},
   540  		{
   541  			title: "Update permissions for existing buckets and apend new ones",
   542  			toACLs: []*authn.BckACL{
   543  				{
   544  					Bck:    newBck("bck", "ais", "1234"),
   545  					Access: 20,
   546  				},
   547  				{
   548  					Bck:    newBck("bck", "ais", "5678"),
   549  					Access: 20,
   550  				},
   551  			},
   552  			fromACLs: []*authn.BckACL{
   553  				{
   554  					Bck:    newBck("bck2", "ais", "1234"),
   555  					Access: 20,
   556  				},
   557  				{
   558  					Bck:    newBck("bck", "ais", "1234"),
   559  					Access: 70,
   560  				},
   561  			},
   562  			resACLs: []*authn.BckACL{
   563  				{
   564  					Bck:    newBck("bck", "ais", "1234"),
   565  					Access: 70,
   566  				},
   567  				{
   568  					Bck:    newBck("bck", "ais", "5678"),
   569  					Access: 20,
   570  				},
   571  				{
   572  					Bck:    newBck("bck2", "ais", "1234"),
   573  					Access: 20,
   574  				},
   575  			},
   576  		},
   577  		{
   578  			title:  "Append and update buckets of '5678' cluster only",
   579  			cluFlt: "5678",
   580  			toACLs: []*authn.BckACL{
   581  				{
   582  					Bck:    newBck("bck", "ais", "1234"),
   583  					Access: 20,
   584  				},
   585  				{
   586  					Bck:    newBck("bck", "ais", "5678"),
   587  					Access: 20,
   588  				},
   589  			},
   590  			fromACLs: []*authn.BckACL{
   591  				{
   592  					Bck:    newBck("bck2", "ais", "5678"),
   593  					Access: 60,
   594  				},
   595  				{
   596  					Bck:    newBck("bck2", "ais", "1234"),
   597  					Access: 70,
   598  				},
   599  				{
   600  					Bck:    newBck("bck", "ais", "5678"),
   601  					Access: 90,
   602  				},
   603  			},
   604  			resACLs: []*authn.BckACL{
   605  				{
   606  					Bck:    newBck("bck", "ais", "1234"),
   607  					Access: 20,
   608  				},
   609  				{
   610  					Bck:    newBck("bck", "ais", "5678"),
   611  					Access: 90,
   612  				},
   613  				{
   614  					Bck:    newBck("bck2", "ais", "5678"),
   615  					Access: 60,
   616  				},
   617  			},
   618  		},
   619  	}
   620  	for _, test := range tests {
   621  		res := mergeBckACLs(test.toACLs, test.fromACLs, test.cluFlt)
   622  		for i, r := range res {
   623  			if !r.Bck.Equal(&test.resACLs[i].Bck) || r.Access != test.resACLs[i].Access {
   624  				t.Errorf("%s[filter: %s]: %v[%v] != %v[%v]", test.title, test.cluFlt, r.Bck, r.Access, test.resACLs[i], test.resACLs[i].Access)
   625  			}
   626  		}
   627  	}
   628  }