github.com/kbehouse/nsc@v0.0.6/cmd/generateprofile_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  	"encoding/json"
    20  	"fmt"
    21  	"net/url"
    22  	"path"
    23  	"testing"
    24  
    25  	"github.com/nats-io/jwt/v2"
    26  
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func Test_ParseNscURLs(t *testing.T) {
    31  	type test struct {
    32  		u    string
    33  		err  bool
    34  		want *NscURL
    35  	}
    36  
    37  	tests := []test{
    38  		{u: "nsc://", err: true, want: nil},
    39  		{u: "http://one", err: true, want: nil},
    40  		{u: "nsc://one", err: false, want: &NscURL{operator: "one"}},
    41  		{u: "nsc://one one/two", err: false, want: &NscURL{operator: "one one", account: "two"}},
    42  		{u: "nsc://one/two/three", err: false, want: &NscURL{operator: "one", account: "two", user: "three"}},
    43  		{u: "nsc://one?hello&world", err: false, want: &NscURL{operator: "one", qs: "hello&world"}},
    44  	}
    45  
    46  	for _, tc := range tests {
    47  		u, err := ParseNscURL(tc.u)
    48  		if tc.err && err == nil {
    49  			t.Fatalf("error parsing %q - expected error, but got none", tc.u)
    50  		}
    51  		if !tc.err && err != nil {
    52  			t.Fatalf("error parsing %q - expected no error, but got %v", tc.u, err)
    53  		}
    54  		require.Equal(t, tc.want, u)
    55  	}
    56  }
    57  
    58  func Test_ParseNscURLQuery(t *testing.T) {
    59  	type test struct {
    60  		u    string
    61  		want map[Arg]string
    62  	}
    63  
    64  	tests := []test{
    65  		{u: "nsc://one?seed&name&key", want: map[Arg]string{seed: "", name: "", key: ""}},
    66  		{u: "nsc://one/two/three?store=/tmp/storedir&keystore=/tmp/key+dir",
    67  			want: map[Arg]string{
    68  				storeDir:    "/tmp/storedir",
    69  				keystoreDir: "/tmp/key dir"}},
    70  	}
    71  
    72  	for _, tc := range tests {
    73  		u, err := ParseNscURL(tc.u)
    74  		if err != nil {
    75  			t.Fatalf("failed parsing query %q: %v", tc.u, err)
    76  		}
    77  		q, err := u.query()
    78  		require.NoError(t, err)
    79  		require.Equal(t, tc.want, q)
    80  	}
    81  }
    82  
    83  func Test_NscURLEncodedNames(t *testing.T) {
    84  	t.Log(url.QueryEscape("My Company Inc."))
    85  	nu, err := ParseNscURL("nsc://My+Company+Inc./Account+Name/")
    86  	require.NoError(t, err)
    87  	o, err := nu.getOperator()
    88  	require.NoError(t, err)
    89  	require.Equal(t, "My Company Inc.", o)
    90  
    91  	a, err := nu.getAccount()
    92  	require.NoError(t, err)
    93  	require.Equal(t, "Account Name", a)
    94  
    95  	u, err := nu.getUser()
    96  	require.NoError(t, err)
    97  	require.Equal(t, "", u)
    98  }
    99  
   100  func loadResults(t *testing.T, out string) Profile {
   101  	d, err := Read(out)
   102  	require.NoError(t, err)
   103  	var r Profile
   104  	require.NoError(t, json.Unmarshal(d, &r))
   105  	return r
   106  }
   107  
   108  func Test_ProfileIDs(t *testing.T) {
   109  	ts := NewTestStore(t, "O")
   110  	defer ts.Done(t)
   111  
   112  	ts.AddAccount(t, "A")
   113  	ts.AddUser(t, "A", "U")
   114  
   115  	o := ts.GetOperatorPublicKey(t)
   116  	a := ts.GetAccountPublicKey(t, "A")
   117  	u := ts.GetUserPublicKey(t, "A", "U")
   118  
   119  	out := path.Join(ts.Dir, "out.json")
   120  	nu := fmt.Sprintf("nsc://%s/%s/%s?operatorName&accountName&userName", o, a, u)
   121  	_, _, err := ExecuteCmd(createProfileCmd(), "-o", out, nu)
   122  	require.NoError(t, err)
   123  
   124  	r := loadResults(t, out)
   125  	require.Equal(t, "O", r.Operator.Name)
   126  	require.Equal(t, "A", r.Account.Name)
   127  	require.Equal(t, "U", r.User.Name)
   128  }
   129  
   130  func Test_ProfileSeedIDs(t *testing.T) {
   131  	ts := NewTestStore(t, "O")
   132  	defer ts.Done(t)
   133  
   134  	// add a signing key
   135  	osk, opk, okp := CreateOperatorKey(t)
   136  	ts.KeyStore.Store(okp)
   137  	oc, err := ts.Store.ReadOperatorClaim()
   138  	require.NoError(t, err)
   139  	kp, err := ts.KeyStore.GetKeyPair(oc.Issuer)
   140  	require.NoError(t, err)
   141  	require.NoError(t, err)
   142  	oc.SigningKeys.Add(opk)
   143  	sjwt, err := oc.Encode(kp)
   144  	require.NoError(t, err)
   145  	_, err = ts.Store.StoreClaim([]byte(sjwt))
   146  	require.NoError(t, err)
   147  
   148  	ts.AddAccount(t, "A")
   149  	ts.AddUser(t, "A", "U")
   150  
   151  	o := ts.GetOperatorPublicKey(t)
   152  	a := ts.GetAccountPublicKey(t, "A")
   153  	u := ts.GetUserPublicKey(t, "A", "U")
   154  
   155  	out := path.Join(ts.Dir, "out.json")
   156  	nu := fmt.Sprintf("nsc://%s/%s/%s?operatorSeed=%s", o, a, u, opk)
   157  	_, _, err = ExecuteCmd(createProfileCmd(), "-o", out, nu)
   158  	require.NoError(t, err)
   159  
   160  	r := loadResults(t, out)
   161  	require.Equal(t, string(osk), r.Operator.Seed)
   162  }
   163  
   164  func Test_ProfileStoreAndKeysDir(t *testing.T) {
   165  	// create store
   166  	ts := NewTestStore(t, "O")
   167  	defer ts.Done(t)
   168  	ts.AddAccount(t, "A")
   169  	ts.AddUser(t, "A", "U")
   170  	opk := ts.GetOperatorPublicKey(t)
   171  	apk := ts.GetAccountPublicKey(t, "A")
   172  	upk := ts.GetUserPublicKey(t, "A", "U")
   173  
   174  	// context for the store is replaced
   175  	ts2 := NewTestStore(t, "OO")
   176  	defer ts2.Done(t)
   177  	ts2.AddAccount(t, "AA")
   178  	ts2.AddUser(t, "AA", "UU")
   179  
   180  	stdout, _, err := ExecuteCmd(rootCmd, "describe", "operator", "--raw")
   181  	require.NoError(t, err)
   182  	ojwt, err := jwt.DecodeOperatorClaims(stdout)
   183  	require.NoError(t, err)
   184  	require.Equal(t, "OO", ojwt.Name)
   185  
   186  	out := path.Join(ts.Dir, "out.json")
   187  	storeDir := path.Join(ts.Dir, "store")
   188  	keyDir := path.Join(ts.Dir, "keys")
   189  	u := fmt.Sprintf("nsc://O/A/U?operatorName&accountName&userName&operatorKey&accountKey&userKey&store=%s&keyStore=%s", storeDir, keyDir)
   190  
   191  	_, _, err = ExecuteCmd(rootCmd, "generate", "profile", "-o", out, u)
   192  	require.NoError(t, err)
   193  	r := loadResults(t, out)
   194  
   195  	require.Equal(t, "O", r.Operator.Name)
   196  	require.Equal(t, opk, r.Operator.Key)
   197  	require.Equal(t, "A", r.Account.Name)
   198  	require.Equal(t, apk, r.Account.Key)
   199  	require.Equal(t, "U", r.User.Name)
   200  	require.Equal(t, upk, r.User.Key)
   201  }
   202  
   203  func TestKey_ProfileBasics(t *testing.T) {
   204  	type test struct {
   205  		u    string
   206  		want []Arg
   207  	}
   208  
   209  	tests := []test{
   210  		{u: "nsc://O?operatorSeed&operatorKey", want: []Arg{operatorKey, operatorSeed}},
   211  		{u: "nsc://O?key&seed", want: []Arg{operatorKey, operatorSeed}},
   212  		{u: "nsc://O/A?key&seed", want: []Arg{accountKey, accountSeed}},
   213  		{u: "nsc://O/A/U?key&seed", want: []Arg{userKey, userSeed}},
   214  	}
   215  
   216  	for _, tc := range tests {
   217  		// create an env matching the u
   218  		u, err := ParseNscURL(tc.u)
   219  		if err != nil {
   220  			t.Fatalf("error parsing %q", tc.u)
   221  		}
   222  		ts := NewTestStore(t, u.operator)
   223  		require.NoError(t, err)
   224  		oseed, err := ts.OperatorKey.Seed()
   225  		require.NoError(t, err)
   226  		opub, err := ts.OperatorKey.PublicKey()
   227  		require.NoError(t, err)
   228  
   229  		var aseed []byte
   230  		var apub string
   231  		if u.account != "" {
   232  			ts.AddAccount(t, u.account)
   233  			akp := ts.GetAccountKey(t, u.account)
   234  			aseed, err = akp.Seed()
   235  			require.NoError(t, err)
   236  			apub, err = akp.PublicKey()
   237  			require.NoError(t, err)
   238  		}
   239  		var useed []byte
   240  		var upub string
   241  		if u.user != "" {
   242  			ts.AddUser(t, u.account, u.user)
   243  			ukp := ts.GetUserKey(t, u.account, u.user)
   244  			useed, err = ukp.Seed()
   245  			require.NoError(t, err)
   246  			upub, err = ukp.PublicKey()
   247  			require.NoError(t, err)
   248  		}
   249  
   250  		// execute the command
   251  		out := path.Join(ts.Dir, "out.json")
   252  		_, _, err = ExecuteCmd(createProfileCmd(), "-o", out, tc.u)
   253  		require.NoError(t, err)
   254  		r := loadResults(t, out)
   255  
   256  		q, err := u.query()
   257  		require.NoError(t, err)
   258  		require.Equal(t, len(tc.want), len(q))
   259  		// check ask keys
   260  		for _, k := range tc.want {
   261  			switch k {
   262  			case operatorKey:
   263  				require.Equal(t, opub, r.Operator.Key)
   264  			case accountKey:
   265  				require.Equal(t, apub, r.Account.Key)
   266  			case userKey:
   267  				require.Equal(t, upub, r.User.Key)
   268  			case key:
   269  				if u.user != "" {
   270  					require.Equal(t, upub, r.User.Key)
   271  				} else if u.account != "" {
   272  					require.Equal(t, apub, r.Account.Key)
   273  				} else {
   274  					require.Equal(t, opub, r.Operator.Key)
   275  				}
   276  			case operatorSeed:
   277  				require.Equal(t, string(oseed), r.Operator.Seed)
   278  			case accountSeed:
   279  				require.Equal(t, string(aseed), r.Account.Seed)
   280  			case userSeed:
   281  				require.Equal(t, string(useed), r.User.Seed)
   282  			case seed:
   283  				if u.user != "" {
   284  					require.Equal(t, string(useed), r.User.Seed)
   285  				} else if u.account != "" {
   286  					require.Equal(t, string(aseed), r.Account.Seed)
   287  				} else {
   288  					require.Equal(t, string(oseed), r.Operator.Seed)
   289  				}
   290  			case storeDir:
   291  			case keystoreDir:
   292  			}
   293  		}
   294  
   295  		ts.Done(t)
   296  	}
   297  }