github.com/kbehouse/nsc@v0.0.6/cmd/util_test.go (about) 1 /* 2 * Copyright 2018-2021 The NATS Authors 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package cmd 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "io/ioutil" 22 "net/http" 23 "net/http/httptest" 24 "net/url" 25 "os" 26 "path/filepath" 27 "regexp" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/kbehouse/nsc/cmd/store" 33 cli "github.com/nats-io/cliprompts/v2" 34 jwt1 "github.com/nats-io/jwt" 35 "github.com/nats-io/jwt/v2" 36 "github.com/nats-io/nats-server/v2/server" 37 nats "github.com/nats-io/nats.go" 38 "github.com/nats-io/nkeys" 39 "github.com/stretchr/testify/require" 40 ) 41 42 type TestStore struct { 43 Dir string 44 45 Store *store.Store 46 KeyStore store.KeyStore 47 48 OperatorKey nkeys.KeyPair 49 OperatorKeyPath string 50 51 Server *server.Server 52 ports *server.Ports 53 Clients []*nats.Conn 54 } 55 56 // Some globals must be reset 57 func ResetForTests() { 58 config = ToolConfig{} 59 ResetSharedFlags() 60 wellKnownOperators = nil 61 } 62 63 func ResetSharedFlags() { 64 KeyPathFlag = "" 65 Json = false 66 Raw = false 67 JsonPath = "" 68 } 69 70 func NewEmptyStore(t *testing.T) *TestStore { 71 ResetForTests() 72 // init runs early 73 SetEnvOptions() 74 var ts TestStore 75 76 homeEnv = t.Name() 77 78 ts.Dir = MakeTempDir(t) 79 require.NoError(t, os.Setenv(t.Name(), filepath.Join(ts.Dir, "toolprefs"))) 80 _, err := initToolHome(t.Name()) 81 require.NoError(t, err) 82 83 // debug the test that created the store 84 _ = ioutil.WriteFile(filepath.Join(ts.Dir, "test.txt"), []byte(t.Name()), 0700) 85 86 err = ForceStoreRoot(t, ts.GetStoresRoot()) 87 require.NoError(t, err) 88 89 nkeysDir := filepath.Join(ts.Dir, "keys") 90 err = os.Mkdir(nkeysDir, 0700) 91 require.NoError(t, err, "error creating %#q", nkeysDir) 92 require.NoError(t, err) 93 err = os.Setenv(store.NKeysPathEnv, nkeysDir) 94 require.NoError(t, err) 95 96 return &ts 97 } 98 99 func NewTestStoreWithOperator(t *testing.T, operatorName string, operator nkeys.KeyPair) *TestStore { 100 ResetForTests() 101 // init runs early 102 SetEnvOptions() 103 var ts TestStore 104 105 // ngsStore is a global - so first test to get it initializes it 106 homeEnv = t.Name() 107 108 ts.OperatorKey = operator 109 ts.Dir = MakeTempDir(t) 110 require.NoError(t, os.Setenv(t.Name(), filepath.Join(ts.Dir, "toolprefs"))) 111 _, err := initToolHome(t.Name()) 112 require.NoError(t, err) 113 // debug the test that created the store 114 _ = ioutil.WriteFile(filepath.Join(ts.Dir, "test.txt"), []byte(t.Name()), 0700) 115 116 err = ForceStoreRoot(t, ts.GetStoresRoot()) 117 require.NoError(t, err) 118 119 ForceOperator(t, operatorName) 120 ts.AddOperatorWithKey(t, operatorName, operator) 121 122 return &ts 123 } 124 125 func NewTestStoreWithOperatorJWT(t *testing.T, operator string) *TestStore { 126 oc, err := jwt.DecodeOperatorClaims(operator) 127 require.NoError(t, err) 128 ts := NewTestStoreWithOperator(t, oc.Name, nil) 129 ts.Store.StoreClaim([]byte(operator)) 130 return ts 131 } 132 133 func (ts *TestStore) Reload(t *testing.T) { 134 s, err := store.LoadStore(ts.Store.Dir) 135 require.NoError(t, err) 136 if ts.Store == nil { 137 ts.Store = s 138 } 139 ctx, err := ts.Store.GetContext() 140 require.NoError(t, err, "getting context") 141 142 ts.KeyStore = ctx.KeyStore 143 144 GetConfig().SetDefaults() 145 } 146 147 func (ts *TestStore) AddOperator(t *testing.T, operatorName string) *store.Store { 148 _, _, kp := CreateOperatorKey(t) 149 return ts.AddOperatorWithKey(t, operatorName, kp) 150 } 151 152 func (ts *TestStore) AddOperatorWithKey(t *testing.T, operatorName string, operator nkeys.KeyPair) *store.Store { 153 storeRoot := ts.GetStoresRoot() 154 operatorRoot := filepath.Join(storeRoot, operatorName) 155 err := os.MkdirAll(operatorRoot, 0700) 156 require.NoError(t, err, "error creating %#q", operatorRoot) 157 158 nkeysDir := filepath.Join(ts.Dir, "keys") 159 _, err = os.Stat(nkeysDir) 160 if err != nil && os.IsNotExist(err) { 161 err = os.Mkdir(nkeysDir, 0700) 162 require.NoError(t, err, "error creating %#q", nkeysDir) 163 } 164 require.NoError(t, err) 165 166 err = os.Setenv(store.NKeysPathEnv, nkeysDir) 167 require.NoError(t, err, "nkeys env") 168 169 var nk = &store.NamedKey{} 170 nk.Name = operatorName 171 nk.KP = operator 172 173 s, err := store.CreateStore(operatorName, storeRoot, nk) 174 require.NoError(t, err) 175 ts.Store = s 176 177 ctx, err := ts.Store.GetContext() 178 require.NoError(t, err, "getting context") 179 180 ts.KeyStore = ctx.KeyStore 181 ts.OperatorKey = operator 182 ts.OperatorKeyPath = "" 183 if operator != nil { 184 ts.OperatorKeyPath, err = ts.KeyStore.Store(operator) 185 require.NoError(t, err, "store operator key") 186 } 187 188 ForceOperator(t, operatorName) 189 190 return s 191 } 192 193 func (ts *TestStore) SwitchOperator(t *testing.T, operator string) { 194 storeRoot := ts.GetStoresRoot() 195 s, err := store.LoadStore(filepath.Join(storeRoot, operator)) 196 require.NoError(t, err) 197 ts.Store = s 198 199 ctx, err := ts.Store.GetContext() 200 require.NoError(t, err, "getting context") 201 ts.KeyStore = ctx.KeyStore 202 203 oc, err := s.LoadRootClaim() 204 require.NoError(t, err) 205 206 kp, err := ts.KeyStore.GetKeyPair(oc.Subject) 207 require.NoError(t, err) 208 209 ts.OperatorKey = kp 210 ts.OperatorKeyPath = "" 211 if kp != nil { 212 ts.OperatorKeyPath = ts.KeyStore.GetKeyPath(oc.Subject) 213 } 214 215 ForceOperator(t, operator) 216 } 217 218 func NewTestStore(t *testing.T, operatorName string) *TestStore { 219 _, _, kp := CreateOperatorKey(t) 220 return NewTestStoreWithOperator(t, operatorName, kp) 221 } 222 223 func TestStoreTree(t *testing.T) { 224 ts := NewTestStore(t, "foo") 225 ts.AddAccount(t, "bar") 226 ts.AddAccount(t, "foo") 227 228 v, err := store.LoadStore(filepath.Join(config.StoreRoot, config.Operator)) 229 require.NoError(t, err) 230 require.NotNil(t, v) 231 } 232 233 func (ts *TestStore) Done(t *testing.T) { 234 for _, nc := range ts.Clients { 235 nc.Close() 236 } 237 if ts.Server != nil { 238 ts.Server.Shutdown() 239 ts.ports = nil 240 } 241 cli.ResetPromptLib() 242 if t.Failed() { 243 t.Log("test artifacts:", ts.Dir) 244 } 245 } 246 247 func (ts *TestStore) GetStoresRoot() string { 248 return filepath.Join(ts.Dir, "store") 249 } 250 251 func (ts *TestStore) AddAccount(t *testing.T, accountName string) { 252 if !ts.Store.Has(store.Accounts, accountName, store.JwtName(accountName)) { 253 _, _, err := ExecuteCmd(CreateAddAccountCmd(), "--name", accountName) 254 require.NoError(t, err) 255 } 256 } 257 258 func (ts *TestStore) AddAccountWithSigner(t *testing.T, accountName string, sk nkeys.KeyPair) { 259 if !ts.Store.Has(store.Accounts, accountName, store.JwtName(accountName)) { 260 seed, err := sk.Seed() 261 require.NoError(t, err) 262 _, _, err = ExecuteCmd(HoistRootFlags(CreateAddAccountCmd()), "--name", accountName, "-K", string(seed)) 263 require.NoError(t, err) 264 } 265 } 266 267 func (ts *TestStore) AddUser(t *testing.T, accountName string, userName string) { 268 ts.AddAccount(t, accountName) 269 _, _, err := ExecuteCmd(CreateAddUserCmd(), "--account", accountName, "--name", userName) 270 require.NoError(t, err) 271 } 272 273 func (ts *TestStore) AddUserWithSigner(t *testing.T, accountName string, userName string, sk nkeys.KeyPair) { 274 ts.AddAccount(t, accountName) 275 seed, err := sk.Seed() 276 require.NoError(t, err) 277 _, _, err = ExecuteCmd(HoistRootFlags(CreateAddUserCmd()), "--account", accountName, "--name", userName, "-K", string(seed)) 278 require.NoError(t, err) 279 } 280 281 func (ts *TestStore) AddExport(t *testing.T, accountName string, kind jwt.ExportType, subject string, public bool) { 282 flags := []string{"--account", accountName, "--subject", subject} 283 if !public { 284 flags = append(flags, "--private") 285 } 286 if kind == jwt.Service { 287 flags = append(flags, "--service") 288 } 289 290 ts.AddAccount(t, accountName) 291 _, _, err := ExecuteCmd(createAddExportCmd(), flags...) 292 require.NoError(t, err) 293 } 294 295 func (ts *TestStore) ImportRequiresToken(t *testing.T, srcAccount string, subject string) bool { 296 ac, err := ts.Store.ReadAccountClaim(srcAccount) 297 require.NoError(t, err) 298 for _, ex := range ac.Exports { 299 if string(ex.Subject) == subject { 300 return ex.TokenReq 301 } 302 } 303 return false 304 } 305 306 func (ts *TestStore) AddImport(t *testing.T, srcAccount string, subject string, targetAccountName string) { 307 flags := []string{"--account", targetAccountName} 308 309 if ts.ImportRequiresToken(t, srcAccount, subject) { 310 token := ts.GenerateActivation(t, srcAccount, subject, targetAccountName) 311 f, err := ioutil.TempFile(ts.Dir, "token") 312 require.NoError(t, err) 313 _, err = f.WriteString(token) 314 require.NoError(t, err) 315 require.NoError(t, f.Close()) 316 flags = append(flags, "--token", f.Name()) 317 } else { 318 flags = append(flags, "--src-account", srcAccount, "--remote-subject", subject) 319 } 320 _, _, err := ExecuteCmd(createAddImportCmd(), flags...) 321 require.NoError(t, err) 322 } 323 324 func (ts *TestStore) GenerateActivation(t *testing.T, srcAccount string, subject string, targetAccount string) string { 325 tpub := ts.GetAccountPublicKey(t, targetAccount) 326 ac, err := ts.Store.ReadAccountClaim(srcAccount) 327 require.NoError(t, err) 328 for _, i := range ac.Exports { 329 if subject == string(i.Subject) { 330 break 331 } 332 } 333 334 flags := []string{"--account", srcAccount, "--target-account", tpub, "--subject", subject} 335 stdout, _, err := ExecuteCmd(createGenerateActivationCmd(), flags...) 336 require.NoError(t, err) 337 token, err := jwt.ParseDecoratedJWT([]byte(stdout)) 338 require.NoError(t, err) 339 return token 340 } 341 342 func (ts *TestStore) GenerateActivationWithSigner(t *testing.T, srcAccount string, subject string, targetAccount string, sk nkeys.KeyPair) string { 343 tpub := ts.GetAccountPublicKey(t, targetAccount) 344 seed, err := sk.Seed() 345 require.NoError(t, err) 346 347 flags := []string{"--account", srcAccount, "--target-account", tpub, "--subject", subject, "-K", string(seed)} 348 stdout, _, err := ExecuteCmd(HoistRootFlags(createGenerateActivationCmd()), flags...) 349 require.NoError(t, err) 350 token, err := jwt.ParseDecoratedJWT([]byte(stdout)) 351 require.NoError(t, err) 352 return token 353 } 354 355 func MakeTempDir(t *testing.T) string { 356 p, err := ioutil.TempDir("", "store_test") 357 require.NoError(t, err) 358 return p 359 } 360 361 func StoreKey(t *testing.T, kp nkeys.KeyPair, dir string) string { 362 p, err := kp.PublicKey() 363 require.NoError(t, err) 364 365 s, err := kp.Seed() 366 require.NoError(t, err) 367 368 fp := filepath.Join(dir, string(p)+".nk") 369 err = ioutil.WriteFile(fp, s, 0600) 370 require.NoError(t, err) 371 return fp 372 } 373 374 func CreateAccountKey(t *testing.T) (seed []byte, pub string, kp nkeys.KeyPair) { 375 return CreateNkey(t, nkeys.PrefixByteAccount) 376 } 377 378 func CreateUserKey(t *testing.T) (seed []byte, pub string, kp nkeys.KeyPair) { 379 return CreateNkey(t, nkeys.PrefixByteUser) 380 } 381 382 func CreateOperatorKey(t *testing.T) (seed []byte, pub string, kp nkeys.KeyPair) { 383 return CreateNkey(t, nkeys.PrefixByteOperator) 384 } 385 386 func CreateNkey(t *testing.T, kind nkeys.PrefixByte) ([]byte, string, nkeys.KeyPair) { 387 kp, err := nkeys.CreatePair(kind) 388 require.NoError(t, err) 389 390 seed, err := kp.Seed() 391 require.NoError(t, err) 392 393 pub, err := kp.PublicKey() 394 require.NoError(t, err) 395 return seed, pub, kp 396 } 397 398 func ForceStoreRoot(t *testing.T, fp string) error { 399 config.StoreRoot = fp 400 return nil 401 } 402 403 func ForceOperator(t *testing.T, operator string) { 404 config.Operator = operator 405 } 406 407 func StripTableDecorations(s string) string { 408 decorations := []string{"╭", "─", "┬", "╮", "├", "│", "┤", "┼", "╰", "┴", "╯", "-", "+", "|"} 409 for _, c := range decorations { 410 s = strings.Replace(s, c, "", -1) 411 } 412 // replace multiple spaces with just one 413 re := regexp.MustCompile(`\s+`) 414 return re.ReplaceAllString(s, " ") 415 } 416 417 func (ts *TestStore) GetAccountKey(t *testing.T, name string) nkeys.KeyPair { 418 ac, err := ts.Store.ReadAccountClaim(name) 419 require.NoError(t, err) 420 kp, err := ts.KeyStore.GetKeyPair(ac.Subject) 421 require.NoError(t, err) 422 return kp 423 } 424 425 func (ts *TestStore) GetUserKey(t *testing.T, account string, name string) nkeys.KeyPair { 426 uc, err := ts.Store.ReadUserClaim(account, name) 427 require.NoError(t, err) 428 kp, err := ts.KeyStore.GetKeyPair(uc.Subject) 429 require.NoError(t, err) 430 return kp 431 } 432 433 func (ts *TestStore) GetAccountKeyPath(t *testing.T, name string) string { 434 sc, err := ts.Store.ReadAccountClaim(name) 435 require.NoError(t, err) 436 return ts.KeyStore.GetKeyPath(sc.Subject) 437 } 438 439 func (ts *TestStore) GetOperatorPublicKey(t *testing.T) string { 440 oc, err := ts.Store.ReadOperatorClaim() 441 require.NoError(t, err) 442 pk, err := ts.KeyStore.GetPublicKey(oc.Subject) 443 require.NoError(t, err) 444 return pk 445 } 446 447 func (ts *TestStore) GetAccountPublicKey(t *testing.T, name string) string { 448 sc, err := ts.Store.ReadAccountClaim(name) 449 require.NoError(t, err) 450 pk, err := ts.KeyStore.GetPublicKey(sc.Subject) 451 require.NoError(t, err) 452 return pk 453 } 454 455 func (ts *TestStore) GetUserPublicKey(t *testing.T, account string, name string) string { 456 sc, err := ts.Store.ReadUserClaim(account, name) 457 require.NoError(t, err) 458 pk, err := ts.KeyStore.GetPublicKey(sc.Subject) 459 require.NoError(t, err) 460 return pk 461 } 462 463 func (ts *TestStore) GetUserSeedKey(t *testing.T, account string, name string) string { 464 sc, err := ts.Store.ReadUserClaim(account, name) 465 require.NoError(t, err) 466 pk, err := ts.KeyStore.GetSeed(sc.Subject) 467 require.NoError(t, err) 468 return pk 469 } 470 471 // Runs a server from a config file, if `Port` is not set it runs at a random port 472 func (ts *TestStore) RunServerWithConfig(t *testing.T, config string) *server.Ports { 473 var opts server.Options 474 require.NoError(t, opts.ProcessConfigFile(config)) 475 return ts.RunServer(t, &opts) 476 } 477 478 // Runs a NATS server at a random port 479 func (ts *TestStore) RunServer(t *testing.T, opts *server.Options) *server.Ports { 480 if opts == nil { 481 opts = &server.Options{ 482 Host: "127.0.0.1", 483 Port: -1, 484 HTTPPort: -1, 485 NoLog: true, 486 NoSigs: true, 487 MaxControlLine: 2048, 488 } 489 } 490 if opts.Port == 0 { 491 opts.Port = -1 492 } 493 if opts.HTTPPort == 0 { 494 opts.HTTPPort = -1 495 } 496 opts.NoLog = true 497 498 var err error 499 ts.Server, err = server.NewServer(opts) 500 require.NoError(t, err) 501 require.NotNil(t, ts.Server) 502 503 if !opts.NoLog { 504 ts.Server.ConfigureLogger() 505 } 506 507 // Run server in Go routine. 508 go ts.Server.Start() 509 510 ts.ports = ts.Server.PortsInfo(10 * time.Second) 511 require.NotNil(t, ts.ports) 512 513 return ts.ports 514 } 515 516 func (ts *TestStore) GetConnz(t *testing.T) *server.Connz { 517 if ts.ports == nil { 518 t.Fatal("not connected") 519 } 520 r, err := http.Get(fmt.Sprintf("%s/connz", ts.ports.Monitoring[0])) 521 require.NoError(t, err) 522 523 defer r.Body.Close() 524 body, err := ioutil.ReadAll(r.Body) 525 require.NoError(t, err) 526 527 var connz server.Connz 528 require.NoError(t, json.Unmarshal(body, &connz)) 529 530 return &connz 531 } 532 533 func (ts *TestStore) CreateClient(t *testing.T, option ...nats.Option) *nats.Conn { 534 if ts.ports == nil { 535 t.Fatal("attempt to create a nats connection without a server running") 536 } 537 nc, err := nats.Connect(strings.Join(ts.ports.Nats, ","), option...) 538 require.NoError(t, err) 539 ts.Clients = append(ts.Clients, nc) 540 return nc 541 } 542 543 func (ts *TestStore) WaitForClient(t *testing.T, name string, subs uint32, maxWait time.Duration) { 544 max := time.Now().Add(maxWait) 545 end := max.Unix() 546 for { 547 connz := ts.GetConnz(t) 548 if connz.NumConns > 0 { 549 for _, v := range connz.Conns { 550 if v.Name == name && v.NumSubs >= subs { 551 return 552 } 553 } 554 } 555 time.Sleep(500 * time.Millisecond) 556 if time.Now().Unix() >= end { 557 t.Fatalf("timed out looking for client %q with %d subs", name, subs) 558 } 559 } 560 } 561 562 func (ts *TestStore) VerifyOperator(t *testing.T, name string, managed bool) { 563 s, err := store.LoadStore(filepath.Join(ts.GetStoresRoot(), name)) 564 require.NoError(t, err) 565 require.NotNil(t, s) 566 567 oc, err := s.ReadOperatorClaim() 568 require.NoError(t, err) 569 require.NotNil(t, oc) 570 require.Equal(t, name, oc.Name) 571 require.Equal(t, managed, s.IsManaged()) 572 573 kp, err := ts.KeyStore.GetKeyPair(oc.Subject) 574 require.NoError(t, err) 575 if managed { 576 require.Nil(t, kp) 577 } else { 578 require.NotNil(t, kp) 579 } 580 } 581 582 func (ts *TestStore) VerifyAccount(t *testing.T, operator string, account string, verifyKeys bool) { 583 s, err := store.LoadStore(filepath.Join(ts.GetStoresRoot(), operator)) 584 require.NoError(t, err) 585 require.NotNil(t, s) 586 587 ac, err := s.ReadAccountClaim(account) 588 require.NoError(t, err) 589 require.NotNil(t, ac) 590 require.Equal(t, account, ac.Name) 591 592 if verifyKeys { 593 old := ts.KeyStore.Env 594 defer func() { 595 ts.KeyStore.Env = old 596 }() 597 ts.KeyStore.Env = operator 598 599 kp, err := ts.KeyStore.GetKeyPair(ac.Subject) 600 require.NoError(t, err) 601 require.NotNil(t, kp) 602 } 603 } 604 605 func (ts *TestStore) VerifyUser(t *testing.T, operator string, account string, user string, verifyKeys bool) { 606 s, err := store.LoadStore(filepath.Join(ts.GetStoresRoot(), operator)) 607 require.NoError(t, err) 608 require.NotNil(t, s) 609 610 uc, err := s.ReadUserClaim(account, user) 611 require.NoError(t, err) 612 require.NotNil(t, uc) 613 require.Equal(t, user, uc.Name) 614 615 if verifyKeys { 616 old := ts.KeyStore.Env 617 defer func() { 618 ts.KeyStore.Env = old 619 }() 620 ts.KeyStore.Env = operator 621 622 kp, err := ts.KeyStore.GetKeyPair(uc.Subject) 623 require.NoError(t, err) 624 require.NotNil(t, kp) 625 sk, err := kp.Seed() 626 require.NoError(t, err) 627 628 fp := ts.KeyStore.CalcUserCredsPath(account, user) 629 _, err = os.Stat(fp) 630 require.NoError(t, err) 631 632 creds, err := Read(fp) 633 require.NoError(t, err) 634 require.Contains(t, string(creds), string(sk)) 635 } 636 } 637 638 func (ts *TestStore) DoesNotExist(t *testing.T, fp string) { 639 _, err := os.Stat(fp) 640 require.True(t, os.IsNotExist(err), fmt.Sprintf("should not exist %s", fp)) 641 } 642 643 func Test_Util(t *testing.T) { 644 ts := NewTestStore(t, "O") 645 defer ts.Done(t) 646 647 oc, err := ts.Store.ReadOperatorClaim() 648 require.NoError(t, err) 649 pk, _ := ts.OperatorKey.PublicKey() 650 require.Equal(t, pk, oc.Subject) 651 652 ts.AddAccount(t, "A") 653 ac, err := ts.Store.ReadAccountClaim("A") 654 require.NoError(t, err) 655 require.Equal(t, oc.Subject, ac.Issuer) 656 657 _, pk, kp := CreateOperatorKey(t) 658 ts.AddOperatorWithKey(t, "OO", kp) 659 oc2, err := ts.Store.ReadOperatorClaim() 660 require.NoError(t, err) 661 require.Equal(t, pk, oc2.Subject) 662 663 ts.AddAccount(t, "AA") 664 ac2, err := ts.Store.ReadAccountClaim("AA") 665 require.NoError(t, err) 666 require.Equal(t, pk, ac2.Issuer) 667 } 668 669 type TasOpts struct { 670 Vers int 671 OperatorOnlyIfV2 bool 672 } 673 674 func RunTestAccountServerWithOperatorKP(t *testing.T, okp nkeys.KeyPair, opts TasOpts) (*httptest.Server, map[string][]byte) { 675 storage := make(map[string][]byte) 676 opk, err := okp.PublicKey() 677 require.NoError(t, err) 678 679 tas := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 680 errHandler := func(w http.ResponseWriter, err error) { 681 w.WriteHeader(http.StatusInternalServerError) 682 w.Write([]byte(err.Error())) 683 } 684 getHandler := func(w http.ResponseWriter, r *http.Request) { 685 var data []byte 686 id := filepath.Base(r.RequestURI) 687 data = storage[id] 688 689 // if we don't have v2, and they ask for v2, ignore it 690 if r.RequestURI == "/jwt/v2/operator" && !opts.OperatorOnlyIfV2 { 691 data = nil 692 } 693 if r.RequestURI == "/jwt/v1/operator" && opts.OperatorOnlyIfV2 { 694 // option to only answer to v2 - they asked v1 695 data = nil 696 } 697 698 if data == nil { 699 w.WriteHeader(http.StatusNotFound) 700 } 701 w.Header().Add("Content-Type", "application/jwt") 702 w.Write(data) 703 if data == nil { 704 t.Log(r.RequestURI, "not found") 705 } else { 706 t.Log(r.RequestURI, "OK") 707 } 708 } 709 710 updateAccountHandler := func(w http.ResponseWriter, r *http.Request) { 711 defer r.Body.Close() 712 body, err := ioutil.ReadAll(r.Body) 713 if err != nil { 714 errHandler(w, err) 715 return 716 } 717 718 ac, err := jwt.DecodeAccountClaims(string(body)) 719 if err != nil { 720 errHandler(w, err) 721 return 722 } 723 724 ok := false 725 if ac.Claims().IsSelfSigned() || ac.Issuer == opk { 726 ok = true 727 } else { 728 ok = ac.SigningKeys.Contains(ac.Issuer) 729 } 730 731 // store a copy of the source jwt that we can inspect 732 orig := fmt.Sprintf("SRC_%s", ac.Subject) 733 storage[orig] = body 734 735 if ok { 736 ac.Limits.Conn = -1 737 ac.Limits.Data = -1 738 ac.Limits.Exports = -1 739 ac.Limits.Imports = -1 740 ac.Limits.LeafNodeConn = -1 741 ac.Limits.Payload = -1 742 ac.Limits.Subs = -1 743 ac.Limits.WildcardExports = true 744 745 token, err := ac.Encode(okp) 746 if err != nil { 747 errHandler(w, err) 748 return 749 } 750 storage[ac.Subject] = []byte(token) 751 752 w.WriteHeader(http.StatusOK) 753 } else { 754 errHandler(w, fmt.Errorf("account %q not self-signed nor by a signer - issuer %q", ac.Subject, ac.Issuer)) 755 } 756 } 757 758 updateActivationHandler := func(w http.ResponseWriter, r *http.Request) { 759 defer r.Body.Close() 760 body, err := ioutil.ReadAll(r.Body) 761 if err != nil { 762 errHandler(w, err) 763 return 764 } 765 766 ac, err := jwt.DecodeActivationClaims(string(body)) 767 if err != nil { 768 errHandler(w, err) 769 return 770 } 771 hid, err := ac.HashID() 772 if err != nil { 773 errHandler(w, err) 774 return 775 } 776 storage[hid] = body 777 w.WriteHeader(http.StatusOK) 778 } 779 780 switch r.Method { 781 case http.MethodGet: 782 getHandler(w, r) 783 case http.MethodPost: 784 p := r.URL.Path 785 if strings.Contains(p, "/accounts/") { 786 updateAccountHandler(w, r) 787 } else if strings.HasSuffix(p, "/activations") { 788 updateActivationHandler(w, r) 789 } 790 default: 791 w.WriteHeader(http.StatusBadRequest) 792 } 793 })) 794 795 if opts.Vers == 1 { 796 oc := jwt1.NewOperatorClaims(opk) 797 oc.Name = "T" 798 oc.Subject = opk 799 u, err := url.Parse(tas.URL) 800 require.NoError(t, err) 801 u.Path = "jwt/v1" 802 oc.AccountServerURL = u.String() 803 token, err := oc.Encode(okp) 804 require.NoError(t, err) 805 storage["operator"] = []byte(token) 806 } else { 807 oc := jwt.NewOperatorClaims(opk) 808 oc.Name = "T" 809 oc.Subject = opk 810 u, err := url.Parse(tas.URL) 811 require.NoError(t, err) 812 u.Path = "jwt/v1" 813 oc.AccountServerURL = u.String() 814 token, err := oc.Encode(okp) 815 require.NoError(t, err) 816 storage["operator"] = []byte(token) 817 } 818 819 return tas, storage 820 } 821 822 // Runs a TestAccountServer returning the server and the underlying storage 823 func RunTestAccountServer(t *testing.T) (*httptest.Server, map[string][]byte) { 824 _, _, okp := CreateOperatorKey(t) 825 return RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2}) 826 }