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  }