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 }