github.com/kbehouse/nsc@v0.0.6/cmd/editoperator_test.go (about)

     1  /*
     2   *
     3   *  * Copyright 2018-2019 The NATS Authors
     4   *  * Licensed under the Apache License, Version 2.0 (the "License");
     5   *  * you may not use this file except in compliance with the License.
     6   *  * You may obtain a copy of the License at
     7   *  *
     8   *  * http://www.apache.org/licenses/LICENSE-2.0
     9   *  *
    10   *  * Unless required by applicable law or agreed to in writing, software
    11   *  * distributed under the License is distributed on an "AS IS" BASIS,
    12   *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *  * See the License for the specific language governing permissions and
    14   *  * limitations under the License.
    15   *
    16   */
    17  
    18  package cmd
    19  
    20  import (
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"testing"
    27  
    28  	"github.com/nats-io/nkeys"
    29  
    30  	"github.com/kbehouse/nsc/cmd/store"
    31  	"github.com/nats-io/jwt/v2"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func Test_EditOperator(t *testing.T) {
    36  	ts := NewTestStore(t, "O")
    37  	defer ts.Done(t)
    38  	ts.AddAccount(t, "TEST")
    39  
    40  	tests := CmdTests{
    41  		{CreateEditOperatorCmd(), []string{"edit", "operator"}, nil, []string{"specify an edit option"}, true},
    42  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "ABTFVAXATJEOKIBESJ3LO3JTAAMDCZ755DLAAGGSDMH5TU6HSFL7YNYY"}, nil, []string{"set system account"}, false},
    43  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "TEST"}, nil, []string{"set system account"}, false},
    44  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "DOESNOTEXIST"}, nil, []string{"account DOESNOTEXIST does not exist in the current operator"}, true},
    45  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--sk"}, nil, []string{"flag needs an argument"}, true},
    46  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--sk", "SAADOZRUTPZS6LIXS6CSSSW5GXY3DNMQMSDTVWHQNHQTIBPGNSADSMBPEU"}, nil, []string{"invalid operator signing key"}, true},
    47  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--sk", "OBMWGGURAFWMH3AFDX65TVIH4ZYSL7UKZ3LOH2ZRWIAU7PGZ3IJNR6W5"}, nil, []string{"edited operator"}, false},
    48  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--tag", "O", "--start", "2019-04-13", "--expiry", "2050-01-01"}, nil, []string{"edited operator"}, false},
    49  		{CreateEditOperatorCmd(), []string{"edit", "operator", "--require-signing-keys"}, nil, []string{"needs to be issued with a signing key first"}, true},
    50  	}
    51  
    52  	tests.Run(t, "root", "edit")
    53  }
    54  
    55  func readJWT(t *testing.T, elem ...string) string {
    56  	t.Helper()
    57  	fp := filepath.Join(elem...)
    58  	require.FileExists(t, fp)
    59  	theJWT, err := ioutil.ReadFile(fp)
    60  	require.NoError(t, err)
    61  	return string(theJWT)
    62  }
    63  
    64  func checkAcc(t *testing.T, ts *TestStore, acc string) {
    65  	t.Helper()
    66  	opJWT := readJWT(t, ts.Dir, "store", "O", "O.jwt")
    67  	op, err := jwt.DecodeOperatorClaims(opJWT)
    68  	require.NoError(t, err)
    69  	require.True(t, op.StrictSigningKeyUsage)
    70  	accJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, fmt.Sprintf("%s.jwt", acc))
    71  	ac, err := jwt.DecodeAccountClaims(accJWT)
    72  	require.NoError(t, err)
    73  	require.NotEqual(t, ac.Issuer, op.Subject)
    74  	require.Equal(t, ac.Issuer, op.SigningKeys[0])
    75  	_, _, err = ExecuteCmd(createValidateCommand(), "--all-accounts")
    76  	require.NoError(t, err)
    77  }
    78  
    79  func checkUsr(t *testing.T, ts *TestStore, acc string) {
    80  	t.Helper()
    81  	opJWT := readJWT(t, ts.Dir, "store", "O", "O.jwt")
    82  	op, err := jwt.DecodeOperatorClaims(opJWT)
    83  	require.NoError(t, err)
    84  	require.True(t, op.StrictSigningKeyUsage)
    85  	accJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, fmt.Sprintf("%s.jwt", acc))
    86  	ac, err := jwt.DecodeAccountClaims(accJWT)
    87  	require.NoError(t, err)
    88  	require.NotEqual(t, ac.Issuer, op.Subject)
    89  	require.Equal(t, ac.Issuer, op.SigningKeys[0])
    90  	usrJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, "users", "U.jwt")
    91  	uc, err := jwt.DecodeUserClaims(usrJWT)
    92  	require.NoError(t, err)
    93  	require.NotEqual(t, uc.Issuer, ac.Subject)
    94  	require.Equal(t, uc.IssuerAccount, ac.Subject)
    95  	require.Equal(t, uc.Issuer, ac.SigningKeys.Keys()[0])
    96  }
    97  
    98  func Test_EditOperatorRequireSigningKeys(t *testing.T) {
    99  	ts := NewEmptyStore(t)
   100  
   101  	_, err := os.Lstat(filepath.Join(ts.Dir, "store"))
   102  	if err != nil && !os.IsNotExist(err) {
   103  		t.Fatal(err)
   104  	}
   105  	// Perform all operations that would end up signing account/user/activation jwt
   106  	_, _, err = ExecuteCmd(CreateAddOperatorCmd(), "--name", "O")
   107  	require.NoError(t, err)
   108  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--sk", "generate")
   109  	require.NoError(t, err)
   110  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--require-signing-keys")
   111  	require.NoError(t, err)
   112  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "EXPORTER")
   113  	require.NoError(t, err)
   114  	checkAcc(t, ts, "EXPORTER")
   115  	_, _, err = ExecuteCmd(createEditAccount(), "--name", "EXPORTER", "--sk", "generate")
   116  	require.NoError(t, err)
   117  	checkAcc(t, ts, "EXPORTER")
   118  	_, _, err = ExecuteCmd(createAddExportCmd(), "--subject", "sub.public")
   119  	require.NoError(t, err)
   120  	checkAcc(t, ts, "EXPORTER")
   121  	_, _, err = ExecuteCmd(createAddExportCmd(), "--subject", "sub.private", "--private")
   122  	require.NoError(t, err)
   123  	checkAcc(t, ts, "EXPORTER")
   124  	_, _, err = ExecuteCmd(createEditExportCmd(), "--account", "EXPORTER", "--subject", "sub.public", "--description", "foo")
   125  	require.NoError(t, err)
   126  	checkAcc(t, ts, "EXPORTER")
   127  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "A")
   128  	require.NoError(t, err)
   129  	checkAcc(t, ts, "A")
   130  	_, _, err = ExecuteCmd(createEditAccount(), "--name", "A", "--sk", "generate")
   131  	require.NoError(t, err)
   132  	checkAcc(t, ts, "A")
   133  	aAc, err := jwt.DecodeAccountClaims(readJWT(t, ts.Dir, "store", "O", "accounts", "A", "A.jwt"))
   134  	require.NoError(t, err)
   135  	expAc, err := jwt.DecodeAccountClaims(readJWT(t, ts.Dir, "store", "O", "accounts", "EXPORTER", "EXPORTER.jwt"))
   136  	require.NoError(t, err)
   137  	outpath := filepath.Join(ts.Dir, "token.jwt")
   138  	_, _, err = ExecuteCmd(createGenerateActivationCmd(), "--account", "EXPORTER", "--subject", "sub.private",
   139  		"--target-account", aAc.Subject, "--output-file", outpath)
   140  	require.NoError(t, err)
   141  	act, err := jwt.DecodeActivationClaims(strings.Split(readJWT(t, outpath), "\n")[1]) // strip decoration
   142  	require.NoError(t, err)
   143  	require.NotEqual(t, act.Issuer, act.IssuerAccount)
   144  	require.Equal(t, act.IssuerAccount, expAc.Subject)
   145  	require.Equal(t, act.Issuer, expAc.SigningKeys.Keys()[0])
   146  	_, _, err = ExecuteCmd(createAddImportCmd(), "--account", "A", "--token", outpath)
   147  	require.NoError(t, err)
   148  	checkAcc(t, ts, "A")
   149  	_, _, err = ExecuteCmd(createAddImportCmd(), "--account", "A", "--src-account", expAc.Subject,
   150  		"--remote-subject", "sub.public")
   151  	require.NoError(t, err)
   152  	checkAcc(t, ts, "A")
   153  	_, _, err = ExecuteCmd(createDeleteImportCmd(), "--account", "A", "--subject", "sub.public")
   154  	require.NoError(t, err)
   155  	checkAcc(t, ts, "A")
   156  	_, _, err = ExecuteCmd(createDeleteExportCmd(), "--account", "EXPORTER", "--subject", "sub.public")
   157  	require.NoError(t, err)
   158  	checkAcc(t, ts, "EXPORTER")
   159  	_, _, err = ExecuteCmd(CreateAddUserCmd(), "--account", "A", "--name", "U")
   160  	require.NoError(t, err)
   161  	checkUsr(t, ts, "A")
   162  	_, _, err = ExecuteCmd(CreateEditUserCmd(), "--account", "A", "--name", "U", "--tag", "foo")
   163  	require.NoError(t, err)
   164  	checkUsr(t, ts, "A")
   165  	_, _, err = ExecuteCmd(CreateDeleteUserCmd(), "--account", "A", "--name", "U", "--revoke")
   166  	require.NoError(t, err)
   167  	checkAcc(t, ts, "A")
   168  	uk, err := nkeys.CreateUser()
   169  	require.NoError(t, err)
   170  	pubUk, err := uk.PublicKey()
   171  	require.NoError(t, err)
   172  	_, _, err = ExecuteCmd(CreateRevokeUserCmd(), "--account", "A", "--user-public-key", pubUk)
   173  	require.NoError(t, err)
   174  	checkAcc(t, ts, "A")
   175  }
   176  
   177  func Test_EditOperatorRequireSigningKeysManaged(t *testing.T) {
   178  	ts := NewEmptyStore(t)
   179  	defer ts.Done(t)
   180  	_, err := os.Lstat(filepath.Join(ts.Dir, "store"))
   181  	if err != nil && !os.IsNotExist(err) {
   182  		t.Fatal(err)
   183  	}
   184  	_, pub, kp := CreateOperatorKey(t)
   185  	oc := jwt.NewOperatorClaims(pub)
   186  	oc.Name = "O"
   187  	oc.StrictSigningKeyUsage = true
   188  	_, psk, sk := CreateOperatorKey(t)
   189  	ts.KeyStore.Store(sk)
   190  	oc.SigningKeys.Add(psk)
   191  	token, err := oc.Encode(kp)
   192  	require.NoError(t, err)
   193  	tf := filepath.Join(ts.Dir, "O.jwt")
   194  	err = Write(tf, []byte(token))
   195  	require.NoError(t, err)
   196  	_, _, err = ExecuteCmd(CreateAddOperatorCmd(), "--url", tf) // causes a managed store
   197  	require.NoError(t, err)
   198  	// perform operations in a managed store and assure identity is not used
   199  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "A")
   200  	require.NoError(t, err)
   201  	checkAcc(t, ts, "A")
   202  	_, _, err = ExecuteCmd(createEditAccount(), "--name", "A", "--sk", "generate")
   203  	require.NoError(t, err)
   204  	checkAcc(t, ts, "A")
   205  	_, _, err = ExecuteCmd(CreateAddUserCmd(), "--account", "A", "--name", "U")
   206  	require.NoError(t, err)
   207  	checkUsr(t, ts, "A")
   208  	_, _, err = ExecuteCmd(CreateEditUserCmd(), "--account", "A", "--name", "U", "--tag", "foo")
   209  	require.NoError(t, err)
   210  	checkUsr(t, ts, "A")
   211  }
   212  
   213  func Test_EditOperatorSigningKeys(t *testing.T) {
   214  	ts := NewTestStore(t, "O")
   215  	defer ts.Done(t)
   216  
   217  	s1, pk1, _ := CreateOperatorKey(t)
   218  	_, pk2, _ := CreateOperatorKey(t)
   219  
   220  	_, _, err := ExecuteCmd(CreateEditOperatorCmd(), "--sk", pk1, "--sk", pk2, "--sk", "generate")
   221  	require.NoError(t, err)
   222  
   223  	d, err := ts.Store.Read(store.JwtName("O"))
   224  	require.NoError(t, err)
   225  
   226  	oc, err := jwt.DecodeOperatorClaims(string(d))
   227  	require.NoError(t, err)
   228  
   229  	require.Contains(t, oc.SigningKeys, pk1)
   230  	require.Contains(t, oc.SigningKeys, pk2)
   231  	require.Len(t, oc.SigningKeys, 3)
   232  
   233  	_, _, err = ExecuteCmd(HoistRootFlags(CreateAddAccountCmd()), "--name", "A", "-K", string(s1))
   234  	require.NoError(t, err)
   235  
   236  	ac, err := ts.Store.ReadAccountClaim("A")
   237  	require.NoError(t, err)
   238  	require.True(t, oc.DidSign(ac))
   239  
   240  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--rm-sk", pk1)
   241  	require.NoError(t, err)
   242  
   243  	d, err = ts.Store.Read(store.JwtName("O"))
   244  	require.NoError(t, err)
   245  
   246  	oc, err = jwt.DecodeOperatorClaims(string(d))
   247  	require.NoError(t, err)
   248  
   249  	require.NotContains(t, oc.SigningKeys, pk1)
   250  	require.Contains(t, oc.SigningKeys, pk2)
   251  	require.False(t, oc.DidSign(ac))
   252  }
   253  
   254  func Test_EditOperatorServiceURLs(t *testing.T) {
   255  	ts := NewTestStore(t, "O")
   256  	defer ts.Done(t)
   257  
   258  	u1 := "nats://localhost:4222"
   259  	u2 := "tls://localhost:4333"
   260  	oc, err := ts.Store.ReadOperatorClaim()
   261  	require.NoError(t, err)
   262  	require.Len(t, oc.OperatorServiceURLs, 0)
   263  
   264  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--service-url", u1, "--service-url", u2)
   265  	require.NoError(t, err)
   266  
   267  	oc, err = ts.Store.ReadOperatorClaim()
   268  	require.NoError(t, err)
   269  	require.Contains(t, oc.OperatorServiceURLs, u1)
   270  	require.Contains(t, oc.OperatorServiceURLs, u2)
   271  
   272  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--rm-service-url", u1)
   273  	require.NoError(t, err)
   274  	oc, err = ts.Store.ReadOperatorClaim()
   275  	require.NoError(t, err)
   276  	require.NotContains(t, oc.OperatorServiceURLs, u1)
   277  	require.Contains(t, oc.OperatorServiceURLs, u2)
   278  }
   279  
   280  func Test_EditOperatorServiceURLsInteractive(t *testing.T) {
   281  	ts := NewTestStore(t, "O")
   282  	defer ts.Done(t)
   283  	ts.AddAccount(t, "SYS")
   284  	pub := ts.GetAccountPublicKey(t, "SYS")
   285  
   286  	u1 := "nats://localhost:4222"
   287  	u2 := "tls://localhost:4333"
   288  	as := "nats://localhost:4222"
   289  
   290  	// valid from, valid until, add tags, acc jwt server, add service url, url, add another, url, add another,
   291  	// system account (defaults to SYS), add signing key
   292  	inputs := []interface{}{"0", "0", true, "xxx", false, as, true, u1, true, u2, false, true, false, false}
   293  
   294  	_, _, err := ExecuteInteractiveCmd(CreateEditOperatorCmd(), inputs)
   295  	require.NoError(t, err)
   296  
   297  	oc, err := ts.Store.ReadOperatorClaim()
   298  	require.NoError(t, err)
   299  	require.Contains(t, oc.OperatorServiceURLs, u1)
   300  	require.Contains(t, oc.OperatorServiceURLs, u2)
   301  	require.Contains(t, oc.Tags, "xxx")
   302  	require.Equal(t, oc.AccountServerURL, as)
   303  	require.Equal(t, oc.SystemAccount, pub)
   304  
   305  	// valid from, valid until, acc jwt server, add service url, remove server urls, add signing key
   306  	inputs = []interface{}{"0", "0", true, []int{0}, false, "", false, true, []int{0}, false, false}
   307  
   308  	_, _, err = ExecuteInteractiveCmd(CreateEditOperatorCmd(), inputs)
   309  	require.NoError(t, err)
   310  	oc, err = ts.Store.ReadOperatorClaim()
   311  	require.NoError(t, err)
   312  	require.NotContains(t, oc.OperatorServiceURLs, u1)
   313  	require.Contains(t, oc.OperatorServiceURLs, u2)
   314  	require.NotContains(t, oc.Tags, "xxx")
   315  	require.Equal(t, oc.AccountServerURL, "")
   316  	require.Equal(t, oc.SystemAccount, pub)
   317  }