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

     1  /*
     2   * Copyright 2018-2020 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  	"fmt"
    20  	"io/ioutil"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/kbehouse/nsc/cmd/store"
    25  	"github.com/nats-io/jwt/v2"
    26  	"github.com/nats-io/nkeys"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func Test_AddAccount(t *testing.T) {
    31  	ts := NewTestStore(t, "add_account")
    32  	defer ts.Done(t)
    33  
    34  	_, bar, _ := CreateAccountKey(t)
    35  	// a cluster key
    36  	ckp, err := nkeys.CreateCluster()
    37  	require.NoError(t, err)
    38  	cpk, err := ckp.PublicKey()
    39  	require.NoError(t, err)
    40  
    41  	tests := CmdTests{
    42  		{CreateAddAccountCmd(), []string{"add", "account"}, nil, []string{"account name is required"}, true},
    43  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "A"}, nil, []string{"generated and stored account key", "added account"}, false},
    44  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "A"}, nil, []string{"the account \"A\" already exists"}, true},
    45  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "B", "--public-key", bar}, nil, nil, false},
    46  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "*"}, nil, []string{"generated and stored account key", "added account"}, false},
    47  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "*"}, nil, []string{"generated and stored account key", "added account"}, false}, // should make a new name
    48  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "X", "--public-key", cpk}, nil, []string{"specified key is not a valid account nkey"}, true},
    49  		{CreateAddAccountCmd(), []string{"add", "account", "--name", "badexp", "--expiry", "30d"}, nil, nil, false},
    50  	}
    51  
    52  	tests.Run(t, "root", "add")
    53  }
    54  
    55  func Test_AddAccountNoStore(t *testing.T) {
    56  	// reset the store
    57  	require.NoError(t, ForceStoreRoot(t, ""))
    58  	_, _, err := ExecuteCmd(CreateAddAccountCmd())
    59  	require.NotNil(t, err)
    60  	require.Equal(t, "no stores available", err.Error())
    61  }
    62  
    63  func Test_AddAccountValidateOutput(t *testing.T) {
    64  	ts := NewTestStore(t, "test")
    65  	defer ts.Done(t)
    66  
    67  	_, _, err := ExecuteCmd(CreateAddAccountCmd(), "--name", "A", "--start", "2018-01-01", "--expiry", "2050-01-01")
    68  	require.NoError(t, err)
    69  	validateAddAccountClaims(t, ts)
    70  }
    71  
    72  func Test_AddAccountInteractive(t *testing.T) {
    73  	ts := NewTestStore(t, "test")
    74  	defer ts.Done(t)
    75  
    76  	inputs := []interface{}{"A", true, "2018-01-01", "2050-01-01", 0}
    77  
    78  	cmd := CreateAddAccountCmd()
    79  	HoistRootFlags(cmd)
    80  	_, _, err := ExecuteInteractiveCmd(cmd, inputs)
    81  	require.NoError(t, err)
    82  	validateAddAccountClaims(t, ts)
    83  }
    84  
    85  func validateAddAccountClaims(t *testing.T, ts *TestStore) {
    86  	ac, err := ts.Store.ReadAccountClaim("A")
    87  	require.NoError(t, err)
    88  
    89  	kp, err := ts.KeyStore.GetKeyPair(ac.Subject)
    90  	require.NoError(t, err)
    91  	_, err = kp.Seed()
    92  	require.NoError(t, err, "stored key should be a seed")
    93  
    94  	pub, err := kp.PublicKey()
    95  	require.NoError(t, err)
    96  	require.Equal(t, ac.Subject, pub, "public key is subject")
    97  
    98  	okp, err := ts.KeyStore.GetKeyPair(ac.Issuer)
    99  	require.NoError(t, err)
   100  	// operator stores will not return a keypair
   101  	if okp == nil {
   102  		okp = kp
   103  	}
   104  
   105  	oppub, err := okp.PublicKey()
   106  	require.NoError(t, err, "getting public key for operator")
   107  	require.Equal(t, ac.Issuer, oppub, "operator signed it")
   108  
   109  	start, err := ParseExpiry("2018-01-01")
   110  	require.NoError(t, err)
   111  	require.Equal(t, start, ac.NotBefore)
   112  
   113  	expire, err := ParseExpiry("2050-01-01")
   114  	require.NoError(t, err)
   115  	require.Equal(t, expire, ac.Expires)
   116  }
   117  
   118  func Test_AddAccountManagedStore(t *testing.T) {
   119  	as, m := RunTestAccountServer(t)
   120  	defer as.Close()
   121  
   122  	ts := NewTestStoreWithOperatorJWT(t, string(m["operator"]))
   123  	defer ts.Done(t)
   124  
   125  	_, _, err := ExecuteCmd(CreateAddAccountCmd(), "--name", "A", "--start", "2018-01-01", "--expiry", "2050-01-01")
   126  	require.NoError(t, err)
   127  }
   128  
   129  func Test_AddAccountManagedStoreWithSigningKey(t *testing.T) {
   130  	ts := NewEmptyStore(t)
   131  	defer ts.Done(t)
   132  	_, pub, kp := CreateOperatorKey(t)
   133  	oc := jwt.NewOperatorClaims(pub)
   134  	oc.Name = "O"
   135  	s1, psk, sk := CreateOperatorKey(t)
   136  	ts.KeyStore.Store(sk)
   137  	oc.SigningKeys.Add(psk)
   138  	token, err := oc.Encode(kp)
   139  	require.NoError(t, err)
   140  	tf := filepath.Join(ts.Dir, "O.jwt")
   141  	err = Write(tf, []byte(token))
   142  	require.NoError(t, err)
   143  	_, _, err = ExecuteCmd(CreateAddOperatorCmd(), "--url", tf)
   144  	require.NoError(t, err)
   145  	// sign with the signing key
   146  	inputs := []interface{}{"A", true, "0", "0", 0, string(s1)}
   147  	_, _, err = ExecuteInteractiveCmd(HoistRootFlags(CreateAddAccountCmd()), inputs)
   148  	require.NoError(t, err)
   149  	accJWT, err := ioutil.ReadFile(filepath.Join(ts.Dir, "store", "O", "accounts", "A", "A.jwt"))
   150  	require.NoError(t, err)
   151  	ac, err := jwt.DecodeAccountClaims(string(accJWT))
   152  	require.NoError(t, err)
   153  	require.False(t, ac.IsSelfSigned())
   154  	require.Equal(t, ac.Issuer, psk)
   155  	require.True(t, oc.DidSign(ac))
   156  	// sign with the account key
   157  	inputs = []interface{}{"B", true, "0", "0", 1, string(s1)}
   158  	_, _, err = ExecuteInteractiveCmd(HoistRootFlags(CreateAddAccountCmd()), inputs)
   159  	require.NoError(t, err)
   160  	accJWT, err = ioutil.ReadFile(filepath.Join(ts.Dir, "store", "O", "accounts", "B", "B.jwt"))
   161  	require.NoError(t, err)
   162  	ac, err = jwt.DecodeAccountClaims(string(accJWT))
   163  	require.NoError(t, err)
   164  	require.True(t, ac.IsSelfSigned())
   165  	require.False(t, oc.DidSign(ac))
   166  }
   167  
   168  func Test_AddAccountInteractiveSigningKey(t *testing.T) {
   169  	ts := NewTestStore(t, "O")
   170  	defer ts.Done(t)
   171  
   172  	s1, pk1, _ := CreateOperatorKey(t)
   173  	_, _, err := ExecuteCmd(CreateEditOperatorCmd(), "--sk", pk1)
   174  	require.NoError(t, err)
   175  
   176  	// sign with the custom key
   177  	inputs := []interface{}{"A", true, "0", "0", 1, string(s1)}
   178  	_, _, err = ExecuteInteractiveCmd(HoistRootFlags(CreateAddAccountCmd()), inputs)
   179  	require.NoError(t, err)
   180  
   181  	d, err := ts.Store.Read(store.JwtName("O"))
   182  	require.NoError(t, err)
   183  	oc, err := jwt.DecodeOperatorClaims(string(d))
   184  	require.NoError(t, err)
   185  
   186  	ac, err := ts.Store.ReadAccountClaim("A")
   187  	require.NoError(t, err)
   188  	require.Equal(t, ac.Issuer, pk1)
   189  	require.True(t, oc.DidSign(ac))
   190  	require.Equal(t, pk1, ac.Issuer)
   191  }
   192  
   193  func Test_AddAccountNameArg(t *testing.T) {
   194  	ts := NewTestStore(t, "O")
   195  	defer ts.Done(t)
   196  
   197  	_, _, err := ExecuteCmd(HoistRootFlags(CreateAddAccountCmd()), "A")
   198  	require.NoError(t, err)
   199  
   200  	_, err = ts.Store.ReadAccountClaim("A")
   201  	require.NoError(t, err)
   202  }
   203  
   204  func Test_AddAccountWithExistingKey(t *testing.T) {
   205  	ts := NewTestStore(t, "O")
   206  	defer ts.Done(t)
   207  
   208  	kp, err := nkeys.CreateAccount()
   209  	require.NoError(t, err)
   210  	_, err = ts.KeyStore.Store(kp)
   211  	require.NoError(t, err)
   212  	pk, err := kp.PublicKey()
   213  	require.NoError(t, err)
   214  
   215  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "A", "--public-key", pk)
   216  	require.NoError(t, err)
   217  }
   218  
   219  func Test_AddManagedAccountWithExistingKey(t *testing.T) {
   220  	as, m := RunTestAccountServer(t)
   221  	defer as.Close()
   222  
   223  	ts := NewTestStoreWithOperatorJWT(t, string(m["operator"]))
   224  	defer ts.Done(t)
   225  
   226  	kp, err := nkeys.CreateAccount()
   227  	require.NoError(t, err)
   228  	_, err = ts.KeyStore.Store(kp)
   229  	require.NoError(t, err)
   230  	pk, err := kp.PublicKey()
   231  	require.NoError(t, err)
   232  
   233  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "A", "--public-key", pk)
   234  	require.NoError(t, err)
   235  
   236  	// inspect the pushed JWT before it was resigned
   237  	ac, err := jwt.DecodeAccountClaims(string(m[fmt.Sprintf("SRC_%s", pk)]))
   238  	require.NoError(t, err)
   239  	require.Equal(t, pk, ac.Subject)
   240  	require.Equal(t, pk, ac.Issuer)
   241  }
   242  
   243  func Test_AddAccountWithSigningKeyOnly(t *testing.T) {
   244  	ts := NewTestStore(t, "O")
   245  	defer ts.Done(t)
   246  
   247  	kp, err := nkeys.CreateOperator()
   248  	require.NoError(t, err)
   249  	_, err = ts.KeyStore.Store(kp)
   250  	require.NoError(t, err)
   251  	pk, err := kp.PublicKey()
   252  	require.NoError(t, err)
   253  	require.True(t, ts.KeyStore.HasPrivateKey(pk))
   254  
   255  	_, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--sk", pk)
   256  	require.NoError(t, err)
   257  	oc, err := ts.Store.ReadOperatorClaim()
   258  	require.NoError(t, err)
   259  	require.NotNil(t, oc)
   260  	require.NoError(t, ts.KeyStore.Remove(oc.Subject))
   261  	require.False(t, ts.KeyStore.HasPrivateKey(oc.Subject))
   262  
   263  	_, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "A")
   264  	require.NoError(t, err)
   265  
   266  	_, err = ts.Store.ReadAccountClaim("A")
   267  	require.NoError(t, err)
   268  }
   269  
   270  func Test_AddAccount_Pubs(t *testing.T) {
   271  	ts := NewTestStore(t, "edit user")
   272  	defer ts.Done(t)
   273  
   274  	_, _, err := ExecuteCmd(CreateAddAccountCmd(), "-n", "A", "--allow-pub", "a,b", "--allow-pubsub", "c", "--deny-pub", "foo", "--deny-pubsub", "bar")
   275  	require.NoError(t, err)
   276  
   277  	cc, err := ts.Store.ReadAccountClaim("A")
   278  	require.NoError(t, err)
   279  	require.NotNil(t, cc)
   280  	require.ElementsMatch(t, cc.DefaultPermissions.Pub.Allow, []string{"a", "b", "c"})
   281  	require.ElementsMatch(t, cc.DefaultPermissions.Sub.Allow, []string{"c"})
   282  	require.ElementsMatch(t, cc.DefaultPermissions.Pub.Deny, []string{"foo", "bar"})
   283  	require.ElementsMatch(t, cc.DefaultPermissions.Sub.Deny, []string{"bar"})
   284  }