github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/test/auth_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  package test
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/tickoalcantara12/micro/v3/client/cli/namespace"
    15  	"github.com/tickoalcantara12/micro/v3/util/config"
    16  )
    17  
    18  // Test no default account generation in non-default namespaces
    19  func TestNoDefaultAccount(t *testing.T) {
    20  	TrySuite(t, testNoDefaultAccount, retryCount)
    21  }
    22  
    23  func testNoDefaultAccount(t *T) {
    24  	t.Parallel()
    25  	serv := NewServer(t, WithLogin())
    26  	defer serv.Close()
    27  	if err := serv.Run(); err != nil {
    28  		return
    29  	}
    30  
    31  	cmd := serv.Command()
    32  
    33  	ns := "random-namespace"
    34  
    35  	err := ChangeNamespace(cmd, serv.Env(), ns)
    36  	if err != nil {
    37  		t.Fatal(err)
    38  		return
    39  	}
    40  
    41  	Try("Log in with user should fail", t, func() ([]byte, error) {
    42  		out, err := serv.Command().Exec("login", "--email", "admin", "--password", "micro")
    43  		if err == nil {
    44  			return out, errors.New("Loggin in should error")
    45  		}
    46  		if strings.Contains(string(out), "Success") {
    47  			return out, errors.New("Loggin in should error")
    48  		}
    49  		return out, nil
    50  	}, 5*time.Second)
    51  
    52  	Try("Run helloworld", t, func() ([]byte, error) {
    53  		outp, err := cmd.Exec("run", "helloworld")
    54  		if err == nil {
    55  			return outp, errors.New("Run should error")
    56  		}
    57  		return outp, nil
    58  	}, 5*time.Second)
    59  
    60  	Try("Find helloworld", t, func() ([]byte, error) {
    61  		outp, err := cmd.Exec("status")
    62  		if err == nil {
    63  			return outp, errors.New("Should not be able to do status")
    64  		}
    65  
    66  		// The started service should have the runtime name of "service/example",
    67  		// as the runtime name is the relative path inside a repo.
    68  		if statusRunning("helloworld", "latest", outp) {
    69  			return outp, errors.New("Shouldn't find example helloworld in runtime")
    70  		}
    71  		return outp, nil
    72  	}, 15*time.Second)
    73  }
    74  
    75  func TestPublicAPI(t *testing.T) {
    76  	TrySuite(t, testPublicAPI, retryCount)
    77  }
    78  
    79  func testPublicAPI(t *T) {
    80  	t.Parallel()
    81  	serv := NewServer(t, WithLogin())
    82  	defer serv.Close()
    83  	if err := serv.Run(); err != nil {
    84  		return
    85  	}
    86  
    87  	cmd := serv.Command()
    88  	outp, err := cmd.Exec("auth", "create", "account", "--secret", "micro", "--scopes", "admin", "--namespace", "random-namespace", "admin")
    89  	if err != nil {
    90  		t.Fatal(string(outp), err)
    91  		return
    92  	}
    93  
    94  	err = ChangeNamespace(cmd, serv.Env(), "random-namespace")
    95  	if err != nil {
    96  		t.Fatal(err)
    97  		return
    98  	}
    99  	// login to admin account
   100  	if err = Login(serv, t, "admin", "micro"); err != nil {
   101  		t.Fatalf("Error logging in %s", err)
   102  		return
   103  	}
   104  
   105  	if err := Try("Run helloworld", t, func() ([]byte, error) {
   106  		return cmd.Exec("run", "./services/helloworld")
   107  	}, 5*time.Second); err != nil {
   108  		return
   109  	}
   110  
   111  	if err := Try("Find helloworld", t, func() ([]byte, error) {
   112  		outp, err := cmd.Exec("status")
   113  		if err != nil {
   114  			return outp, err
   115  		}
   116  
   117  		// The started service should have the runtime name of "service/example",
   118  		// as the runtime name is the relative path inside a repo.
   119  		if !statusRunning("helloworld", "latest", outp) {
   120  			return outp, errors.New("Can't find example helloworld in runtime")
   121  		}
   122  		return outp, err
   123  	}, 15*time.Second); err != nil {
   124  		return
   125  	}
   126  
   127  	if err := Try("Call helloworld", t, func() ([]byte, error) {
   128  		outp, err := cmd.Exec("helloworld", "--name=joe")
   129  		if err != nil {
   130  			outp1, _ := cmd.Exec("logs", "helloworld")
   131  			return append(outp, outp1...), err
   132  		}
   133  		if !strings.Contains(string(outp), "Msg") {
   134  			return outp, err
   135  		}
   136  		return outp, err
   137  	}, 90*time.Second); err != nil {
   138  		return
   139  	}
   140  
   141  	if err := Try("curl helloworld", t, func() ([]byte, error) {
   142  		bod, rsp, err := curl(serv, "random-namespace", "helloworld?name=Jane")
   143  		if rsp == nil {
   144  			return []byte(bod), fmt.Errorf("helloworld should have response, err: %v", err)
   145  		}
   146  		if _, ok := rsp["message"].(string); !ok {
   147  			return []byte(bod), fmt.Errorf("Helloworld is not saying hello, response body: '%v'", bod)
   148  		}
   149  		return []byte(bod), nil
   150  	}, 90*time.Second); err != nil {
   151  		return
   152  	}
   153  }
   154  
   155  func TestServerAuth(t *testing.T) {
   156  	TrySuite(t, ServerAuth, retryCount)
   157  }
   158  
   159  func ServerAuth(t *T) {
   160  	t.Parallel()
   161  	serv := NewServer(t, WithLogin())
   162  	defer serv.Close()
   163  	if err := serv.Run(); err != nil {
   164  		return
   165  	}
   166  
   167  	cmd := serv.Command()
   168  
   169  	// Execute first command in read to wait for store service
   170  	// to start up
   171  	if err := Try("Calling micro auth list accounts", t, func() ([]byte, error) {
   172  		outp, err := cmd.Exec("auth", "list", "accounts")
   173  		if err != nil {
   174  			return outp, err
   175  		}
   176  		if !strings.Contains(string(outp), "admin") {
   177  			return outp, fmt.Errorf("Output should contain default admin account")
   178  		}
   179  		return outp, nil
   180  	}, 15*time.Second); err != nil {
   181  		return
   182  	}
   183  
   184  	if err := Try("Calling micro auth list rules", t, func() ([]byte, error) {
   185  		outp, err := cmd.Exec("auth", "list", "rules")
   186  		if err != nil {
   187  			return outp, err
   188  		}
   189  		if !strings.Contains(string(outp), "default") {
   190  			return outp, fmt.Errorf("Output should contain default rule")
   191  		}
   192  		return outp, nil
   193  	}, 8*time.Second); err != nil {
   194  		return
   195  	}
   196  
   197  	if err := Try("Try to get token with default account", t, func() ([]byte, error) {
   198  		outp, err := cmd.Exec("call", "auth", "Auth.Token", `{"id":"admin","secret":"micro"}`)
   199  		if err != nil {
   200  			return outp, err
   201  		}
   202  		rsp := map[string]interface{}{}
   203  		err = json.Unmarshal(outp, &rsp)
   204  		token, ok := rsp["token"].(map[string]interface{})
   205  		if !ok {
   206  			return outp, errors.New("Can't find token")
   207  		}
   208  		if _, ok = token["access_token"].(string); !ok {
   209  			return outp, fmt.Errorf("Can't find access token")
   210  		}
   211  		if _, ok = token["refresh_token"].(string); !ok {
   212  			return outp, fmt.Errorf("Can't find access token")
   213  		}
   214  		if _, ok = token["refresh_token"].(string); !ok {
   215  			return outp, fmt.Errorf("Can't find refresh token")
   216  		}
   217  		if _, ok = token["expiry"].(string); !ok {
   218  			return outp, fmt.Errorf("Can't find access token")
   219  		}
   220  		return outp, nil
   221  	}, 8*time.Second); err != nil {
   222  		return
   223  	}
   224  }
   225  
   226  func TestServerLockdown(t *testing.T) {
   227  	TrySuite(t, testServerLockdown, retryCount)
   228  }
   229  
   230  func testServerLockdown(t *T) {
   231  	t.Parallel()
   232  	serv := NewServer(t, WithLogin())
   233  	defer serv.Close()
   234  	if err := serv.Run(); err != nil {
   235  		return
   236  	}
   237  
   238  	lockdownSuite(serv, t)
   239  }
   240  
   241  func lockdownSuite(serv Server, t *T) {
   242  	cmd := serv.Command()
   243  
   244  	// Execute first command in read to wait for store service
   245  	// to start up
   246  	ns, err := namespace.Get(serv.Env())
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	t.Log("Namespace is", ns)
   251  
   252  	_, rsp, _ := curl(serv, "micro", "store/list")
   253  	if rsp == nil {
   254  		t.Fatal(rsp, errors.New("store list should have response"))
   255  	}
   256  	if val, ok := rsp["Code"].(float64); !ok || val != 401 {
   257  		t.Fatal(rsp, errors.New("store list should be closed"), val)
   258  	}
   259  
   260  	Login(serv, t, "admin", "micro")
   261  
   262  	email := "me@email.com"
   263  	pass := "mystrongpass"
   264  
   265  	outp, err := cmd.Exec("auth", "create", "account", "--secret", pass, email)
   266  	if err != nil {
   267  		t.Fatal(string(outp), err)
   268  		return
   269  	}
   270  
   271  	outp, err = cmd.Exec("auth", "create", "rule", "--access=granted", "--scope=*", "--resource=*:*:*", "onlyloggedin")
   272  	if err != nil {
   273  		t.Fatal(string(outp), err)
   274  		return
   275  	}
   276  
   277  	outp, err = cmd.Exec("auth", "create", "rule", "--access=granted", "--resource=service:auth:Auth.Token", "authpublic")
   278  	if err != nil {
   279  		t.Fatal(string(outp), err)
   280  		return
   281  	}
   282  
   283  	outp, err = cmd.Exec("auth", "delete", "rule", "admin")
   284  	if err != nil {
   285  		t.Fatal(string(outp), err)
   286  		return
   287  	}
   288  
   289  	// set the local config file to be the same as the one micro will be configured to use.
   290  	// todo: consider adding a micro logout command.
   291  	config.SetConfig(cmd.Config)
   292  	outp, err = cmd.Exec("logout")
   293  	if err != nil {
   294  		t.Fatal(string(outp))
   295  		return
   296  	}
   297  
   298  	if err := Try("Listing rules should fail before login", t, func() ([]byte, error) {
   299  		outp, err := cmd.Exec("auth", "list", "rules")
   300  		if err == nil {
   301  			return outp, errors.New("List rules should fail")
   302  		}
   303  		return outp, nil
   304  	}, 40*time.Second); err != nil {
   305  		return
   306  	}
   307  
   308  	// auth rules are cached so this could take a few seconds (until the authpublic rule takes
   309  	// effect in both the proxy and the auth service)
   310  	if err := Try("Logging in with "+email, t, func() ([]byte, error) {
   311  		out, err := cmd.Exec("login", "--email", email, "--password", pass)
   312  		if err != nil {
   313  			return out, err
   314  		}
   315  		if !strings.Contains(string(out), "Success") {
   316  			return out, errors.New("Login output does not contain 'Success'")
   317  		}
   318  		return out, err
   319  	}, 40*time.Second); err != nil {
   320  		return
   321  	}
   322  
   323  	if err := Try("Listing rules should pass after login", t, func() ([]byte, error) {
   324  		outp, err := cmd.Exec("auth", "list", "rules")
   325  		if err != nil {
   326  			return outp, err
   327  		}
   328  		if !strings.Contains(string(outp), "onlyloggedin") || !strings.Contains(string(outp), "authpublic") {
   329  			return outp, errors.New("Can't find rules")
   330  		}
   331  		return outp, err
   332  	}, 40*time.Second); err != nil {
   333  		return
   334  	}
   335  }
   336  
   337  func TestPasswordChange(t *testing.T) {
   338  	TrySuite(t, changePassword, retryCount)
   339  }
   340  
   341  func changePassword(t *T) {
   342  	t.Parallel()
   343  	serv := NewServer(t, WithLogin())
   344  	defer serv.Close()
   345  	if err := serv.Run(); err != nil {
   346  		return
   347  	}
   348  
   349  	cmd := serv.Command()
   350  	newPass := "shinyNewPass"
   351  
   352  	email := "me@email.com"
   353  	pass := "mystrongpass"
   354  
   355  	outp, err := cmd.Exec("auth", "create", "account", "--secret", pass, email)
   356  	if err != nil {
   357  		t.Fatal(string(outp), err)
   358  		return
   359  	}
   360  
   361  	Login(serv, t, email, pass)
   362  
   363  	// Bad password should not succeed
   364  	outp, err = cmd.Exec("user", "set", "password", "--old-password", "micro121212", "--new-password", newPass)
   365  	if err == nil {
   366  		t.Fatal("Incorrect existing password should make password change fail")
   367  		return
   368  	}
   369  
   370  	outp, err = cmd.Exec("user", "set", "password", "--old-password", pass, "--new-password", newPass)
   371  	if err != nil {
   372  		t.Fatal(string(outp))
   373  		return
   374  	}
   375  
   376  	time.Sleep(3 * time.Second)
   377  	outp, err = cmd.Exec("login", "--email", email, "--password", pass)
   378  	if err == nil {
   379  		t.Fatal("Old password should not be usable anymore")
   380  		return
   381  	}
   382  	outp, err = cmd.Exec("login", "--email", email, "--password", newPass)
   383  	if err != nil {
   384  		t.Fatal(string(outp))
   385  		return
   386  	}
   387  }
   388  
   389  // TestUsernameLogin tests whether we can login using both ID and username e.g. UUID and email
   390  func TestUsernameLogin(t *testing.T) {
   391  	TrySuite(t, testUsernameLogin, retryCount)
   392  }
   393  
   394  func testUsernameLogin(t *T) {
   395  	t.Parallel()
   396  	serv := NewServer(t, WithLogin())
   397  	defer serv.Close()
   398  	if err := serv.Run(); err != nil {
   399  		return
   400  	}
   401  
   402  	cmd := serv.Command()
   403  	outp, err := cmd.Exec("call", "auth", "Auth.Generate", `{"id":"someID", "name":"someUsername", "secret":"password", "scopes": ["admin"] }`)
   404  	if err != nil {
   405  		t.Fatalf("Error generating account %s %s", string(outp), err)
   406  	}
   407  	outp, err = cmd.Exec("login", "--username", "someUsername", "--password", "password")
   408  	if err != nil {
   409  		t.Fatalf("Error logging in with user name %s %s", string(outp), err)
   410  	}
   411  	outp, err = cmd.Exec("login", "--username", "someID", "--password", "password")
   412  	if err != nil {
   413  		t.Fatalf("Error logging in with ID %s %s", string(outp), err)
   414  	}
   415  	// test the email alias
   416  	outp, err = cmd.Exec("login", "--email", "someID", "--password", "password")
   417  	if err != nil {
   418  		t.Fatalf("Error logging in with ID %s %s", string(outp), err)
   419  	}
   420  
   421  	// test we can't create an account with the same name but different ID
   422  	outp, err = cmd.Exec("call", "auth", "Auth.Generate", `{"id":"someID2", "name":"someUsername", "secret":"password1"}`)
   423  	if err == nil {
   424  		// shouldn't let us create something with the same username
   425  		t.Fatalf("Expected error when generating account %s %s", string(outp), err)
   426  	}
   427  
   428  	outp, err = cmd.Exec("auth", "list", "accounts")
   429  	if err != nil {
   430  		t.Fatalf("Error listing accounts %s %s", string(outp), err)
   431  	}
   432  	if !strings.Contains(string(outp), "someUsername") {
   433  		t.Fatalf("Error listing accounts, name is missing from %s", string(outp))
   434  	}
   435  
   436  	outp, err = cmd.Exec("login", "--username", "someID", "--password", "password")
   437  	if err != nil {
   438  		t.Fatalf("Error logging in with ID %s %s", string(outp), err)
   439  	}
   440  
   441  	// make sure user sees username and not ID
   442  	outp, err = cmd.Exec("user")
   443  	if err != nil {
   444  		t.Fatalf("Error running user command %s %s", string(outp), err)
   445  	}
   446  	if !strings.Contains(string(outp), "someUsername") {
   447  		t.Fatalf("Error running user command. Unexpected result %s", string(outp))
   448  	}
   449  	// make sure user sees username and not ID
   450  	outp, err = cmd.Exec("user", "config")
   451  	if err != nil {
   452  		t.Fatalf("Error running user config command %s %s", string(outp), err)
   453  	}
   454  	if !strings.Contains(string(outp), "someUsername") {
   455  		t.Fatalf("Error running user config command. Unexpected result %s", string(outp))
   456  	}
   457  	// make sure change password works correctly for username
   458  	outp, err = cmd.Exec("user", "set", "password", "--old-password", "password", "--new-password", "password1")
   459  	if err != nil {
   460  		t.Fatalf("Error changing password %s %s", string(outp), err)
   461  	}
   462  
   463  	outp, err = cmd.Exec("login", "--username", "someUsername", "--password", "password1")
   464  	if err != nil {
   465  		t.Fatalf("Error changing password %s %s", string(outp), err)
   466  	}
   467  
   468  	outp, err = cmd.Exec("run", "github.com/micro/examples/helloworld")
   469  	if err != nil {
   470  		t.Fatalf("Error running helloworld %s %s", string(outp), err)
   471  	}
   472  	Try("Check helloworld status", t, func() ([]byte, error) {
   473  		outp, err = cmd.Exec("status")
   474  		if err != nil {
   475  			return outp, fmt.Errorf("Error getting status %s", err)
   476  		}
   477  		if !strings.Contains(string(outp), "owner=someUsername") {
   478  			return outp, fmt.Errorf("Can't find owner")
   479  		}
   480  		return nil, nil
   481  	}, 30*time.Second)
   482  
   483  }