github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/pull_test.go (about)

     1  /*
     2   * Copyright 2018-2023 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  	"bytes"
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/nats-io/jwt/v2"
    27  	"github.com/nats-io/nkeys"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func editAccount(t *testing.T, kp nkeys.KeyPair, d []byte, tag string) []byte {
    32  	ac, err := jwt.DecodeAccountClaims(string(d))
    33  	require.NoError(t, err)
    34  	ac.Tags.Add(tag)
    35  	token, err := ac.Encode(kp)
    36  	require.NoError(t, err)
    37  	return []byte(token)
    38  }
    39  
    40  func Test_SyncAccount(t *testing.T) {
    41  	// run a jwt account server
    42  	as, m := RunTestAccountServer(t)
    43  	defer as.Close()
    44  
    45  	ts := NewTestStoreWithOperatorJWT(t, string(m["operator"]))
    46  	defer ts.Done(t)
    47  
    48  	ts.AddAccount(t, "A")
    49  	kp := ts.GetAccountKey(t, "A")
    50  	pk, err := kp.PublicKey()
    51  	require.NoError(t, err)
    52  
    53  	d := editAccount(t, kp, m[pk], "test")
    54  	m[pk] = d
    55  
    56  	_, _, err = ExecuteCmd(createPullCmd())
    57  	require.NoError(t, err)
    58  
    59  	ac, err := ts.Store.ReadAccountClaim("A")
    60  	require.NoError(t, err)
    61  	require.Contains(t, ac.Tags, "test")
    62  }
    63  
    64  func Test_SyncMultipleAccount(t *testing.T) {
    65  	as, m := RunTestAccountServer(t)
    66  	defer as.Close()
    67  
    68  	ts := NewTestStoreWithOperatorJWT(t, string(m["operator"]))
    69  	defer ts.Done(t)
    70  
    71  	ts.AddAccount(t, "A")
    72  	kp := ts.GetAccountKey(t, "A")
    73  	pk, err := kp.PublicKey()
    74  	require.NoError(t, err)
    75  	d := editAccount(t, kp, m[pk], "test")
    76  	m[pk] = d
    77  
    78  	ts.AddAccount(t, "B")
    79  	kp = ts.GetAccountKey(t, "B")
    80  	pk, err = kp.PublicKey()
    81  	require.NoError(t, err)
    82  	d = editAccount(t, kp, m[pk], "test")
    83  	m[pk] = d
    84  
    85  	_, _, err = ExecuteCmd(createPullCmd(), "--all")
    86  	require.NoError(t, err)
    87  
    88  	ac, err := ts.Store.ReadAccountClaim("A")
    89  	require.NoError(t, err)
    90  	require.Contains(t, ac.Tags, "test")
    91  
    92  	ac, err = ts.Store.ReadAccountClaim("B")
    93  	require.NoError(t, err)
    94  	require.Contains(t, ac.Tags, "test")
    95  }
    96  
    97  func Test_SyncNoAccountServer(t *testing.T) {
    98  	ts := NewTestStore(t, "O")
    99  	ts.AddAccount(t, "A")
   100  
   101  	_, _, err := ExecuteCmd(createPullCmd())
   102  	require.Error(t, err)
   103  }
   104  
   105  func Test_SyncNewer(t *testing.T) {
   106  	as, m := RunTestAccountServer(t)
   107  	defer as.Close()
   108  
   109  	ts := NewTestStoreWithOperatorJWT(t, string(m["operator"]))
   110  	defer ts.Done(t)
   111  
   112  	ts.AddAccount(t, "A")
   113  	kp := ts.GetAccountKey(t, "A")
   114  	pk, err := kp.PublicKey()
   115  	require.NoError(t, err)
   116  	time.Sleep(time.Second * 2)
   117  	// the client is supposed to update the remote server
   118  	// so this is really just an edge case - we save a newer
   119  	// one than the server has to create the issue
   120  	err = ts.Store.StoreRaw(editAccount(t, kp, m[pk], "test"))
   121  	require.NoError(t, err)
   122  	ac, err := ts.Store.ReadAccountClaim("A")
   123  	require.NoError(t, err)
   124  	require.Contains(t, ac.Tags, "test")
   125  
   126  	_, _, err = ExecuteCmd(createPullCmd())
   127  	require.Error(t, err)
   128  
   129  	ac, err = ts.Store.ReadAccountClaim("A")
   130  	require.NoError(t, err)
   131  	require.Contains(t, ac.Tags, "test")
   132  
   133  	// now allow the overwrite
   134  	_, _, err = ExecuteCmd(createPullCmd(), "--overwrite-newer")
   135  	if err != nil {
   136  		panic(err)
   137  	}
   138  	require.NoError(t, err)
   139  	ac, err = ts.Store.ReadAccountClaim("A")
   140  	require.NoError(t, err)
   141  	require.Empty(t, ac.Tags)
   142  }
   143  
   144  func Test_SyncNewerFromNatsResolver(t *testing.T) {
   145  	ts := NewEmptyStore(t)
   146  	defer ts.Done(t)
   147  	_, _, err := ExecuteCmd(createAddOperatorCmd(), "--name", "OP", "--sys")
   148  	require.NoError(t, err)
   149  	ts.SwitchOperator(t, "OP") // switch the operator so ts is in a usable state to obtain operator key
   150  	serverconf := filepath.Join(ts.Dir, "server.conf")
   151  	_, _, err = ExecuteCmd(createServerConfigCmd(), "--nats-resolver", "--config-file", serverconf)
   152  	require.NoError(t, err)
   153  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC1")
   154  	require.NoError(t, err)
   155  	// modify the generated file so testing becomes easier by knowing where the jwt directory is
   156  	data, err := os.ReadFile(serverconf)
   157  	require.NoError(t, err)
   158  	dir := ts.AddSubDir(t, "resolver")
   159  	data = bytes.ReplaceAll(data, []byte(`dir: './jwt'`), []byte(fmt.Sprintf(`dir: '%s'`, dir)))
   160  	t.Log(string(data))
   161  	err = os.WriteFile(serverconf, data, 0660)
   162  	require.NoError(t, err)
   163  	// Create a new account, only known to the nats-server. This account can be pulled
   164  	opKey, err := ts.Store.GetRootPublicKey()
   165  	require.NoError(t, err)
   166  	opKp, err := ts.KeyStore.GetKeyPair(opKey)
   167  	require.NoError(t, err)
   168  	acKp, err := nkeys.CreateAccount()
   169  	require.NoError(t, err)
   170  	subj, err := acKp.PublicKey()
   171  	require.NoError(t, err)
   172  	claimOrig := jwt.NewAccountClaims(subj)
   173  	claimOrig.Name = "acc-name"
   174  	theJwtToPull, err := claimOrig.Encode(opKp)
   175  	require.NoError(t, err)
   176  	os.WriteFile(dir+string(os.PathSeparator)+subj+".jwt", []byte(theJwtToPull), 0660)
   177  	ports := ts.RunServerWithConfig(t, serverconf)
   178  	require.NotNil(t, ports)
   179  	// only after server start as ports are not yet known in tests
   180  	_, _, err = ExecuteCmd(createEditOperatorCmd(), "--account-jwt-server-url", ports.Nats[0])
   181  	require.NoError(t, err)
   182  	_, _, err = ExecuteCmd(createPullCmd(), "--all")
   183  	require.NoError(t, err)
   184  	// again, this time with system account and user specified
   185  	_, _, err = ExecuteCmd(createPullCmd(), "--all", "--system-account", "SYS", "--system-user", "sys")
   186  	require.NoError(t, err)
   187  	// claim now exists in nsc store
   188  	claim2, err := ts.Store.ReadAccountClaim("acc-name")
   189  	require.NoError(t, err)
   190  	require.NotEmpty(t, claimOrig.ID)
   191  	require.Equal(t, claimOrig.ID, claim2.ID)
   192  }
   193  
   194  func Test_SyncNewerFromNatsResolverWs(t *testing.T) {
   195  	ts := NewEmptyStore(t)
   196  	defer ts.Done(t)
   197  	_, _, err := ExecuteCmd(createAddOperatorCmd(), "--name", "OP", "--sys")
   198  	require.NoError(t, err)
   199  	ts.SwitchOperator(t, "OP") // switch the operator so ts is in a usable state to obtain operator key
   200  	serverconf := filepath.Join(ts.Dir, "server.conf")
   201  	_, _, err = ExecuteCmd(createServerConfigCmd(), "--nats-resolver", "--config-file", serverconf)
   202  	require.NoError(t, err)
   203  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC1")
   204  	require.NoError(t, err)
   205  	// modify the generated file so testing becomes easier by knowing where the jwt directory is
   206  	data, err := os.ReadFile(serverconf)
   207  	require.NoError(t, err)
   208  	dir := ts.AddSubDir(t, "resolver")
   209  	ws := `websocket: { 
   210    port: -1 
   211    no_tls: true
   212  }`
   213  	data = append(data, ws...)
   214  	data = bytes.ReplaceAll(data, []byte(`dir: './jwt'`), []byte(fmt.Sprintf(`dir: '%s'`, dir)))
   215  	err = os.WriteFile(serverconf, data, 0660)
   216  	require.NoError(t, err)
   217  	// Create a new account, only known to the nats-server. This account can be pulled
   218  	opKey, err := ts.Store.GetRootPublicKey()
   219  	require.NoError(t, err)
   220  	opKp, err := ts.KeyStore.GetKeyPair(opKey)
   221  	require.NoError(t, err)
   222  	acKp, err := nkeys.CreateAccount()
   223  	require.NoError(t, err)
   224  	subj, err := acKp.PublicKey()
   225  	require.NoError(t, err)
   226  	claimOrig := jwt.NewAccountClaims(subj)
   227  	claimOrig.Name = "acc-name"
   228  	theJwtToPull, err := claimOrig.Encode(opKp)
   229  	require.NoError(t, err)
   230  	os.WriteFile(dir+string(os.PathSeparator)+subj+".jwt", []byte(theJwtToPull), 0660)
   231  	ports := ts.RunServerWithConfig(t, serverconf)
   232  	require.NotNil(t, ports)
   233  	// only after server start as ports are not yet known in tests
   234  	_, _, err = ExecuteCmd(createEditOperatorCmd(), "--account-jwt-server-url", ports.WebSocket[0])
   235  	require.NoError(t, err)
   236  
   237  	_, _, err = ExecuteCmd(createPullCmd(), "--all")
   238  	require.NoError(t, err)
   239  	// again, this time with system account and user specified
   240  	_, _, err = ExecuteCmd(createPullCmd(), "--all", "--system-account", "SYS", "--system-user", "sys")
   241  	require.NoError(t, err)
   242  	// claim now exists in nsc store
   243  	claim2, err := ts.Store.ReadAccountClaim("acc-name")
   244  	require.NoError(t, err)
   245  	require.NotEmpty(t, claimOrig.ID)
   246  	require.Equal(t, claimOrig.ID, claim2.ID)
   247  }
   248  
   249  func Test_V2OperatorDoesntFail(t *testing.T) {
   250  	_, _, okp := CreateOperatorKey(t)
   251  	as, m := RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2, OperatorOnlyIfV2: true})
   252  	defer as.Close()
   253  
   254  	ts := NewTestStoreWithOperator(t, "T", okp)
   255  	defer ts.Done(t)
   256  	err := ts.Store.StoreRaw(m["operator"])
   257  	require.NoError(t, err)
   258  
   259  	// edit the jwt
   260  	_, _, err = ExecuteCmd(createPullCmd(), "-A")
   261  	require.NoError(t, err)
   262  
   263  	oc, err := ts.Store.ReadOperatorClaim()
   264  	require.NoError(t, err)
   265  	require.Equal(t, oc.Version, 2)
   266  }
   267  
   268  func Test_V1OperatorDoesntFail(t *testing.T) {
   269  	_, _, okp := CreateOperatorKey(t)
   270  	as, m := RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2})
   271  	defer as.Close()
   272  
   273  	ts := NewTestStoreWithOperator(t, "T", okp)
   274  	defer ts.Done(t)
   275  	err := ts.Store.StoreRaw(m["operator"])
   276  	require.NoError(t, err)
   277  
   278  	// edit the jwt
   279  	stdout, stderr, err := ExecuteCmd(createPullCmd(), "-A")
   280  	t.Log(stdout)
   281  	t.Log(stderr)
   282  	require.NoError(t, err)
   283  
   284  	oc, err := ts.Store.ReadOperatorClaim()
   285  	require.NoError(t, err)
   286  	require.Equal(t, oc.Version, 2)
   287  }