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