github.com/kbehouse/nsc@v0.0.6/cmd/upgradejwt_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 "archive/zip" 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "testing" 26 27 "github.com/kbehouse/nsc/cmd/store" 28 jwtv1 "github.com/nats-io/jwt" 29 "github.com/nats-io/jwt/v2" 30 "github.com/nats-io/nkeys" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func storeOperatorKey(t *testing.T, ts *TestStore, kp nkeys.KeyPair) { 35 t.Helper() 36 ts.OperatorKey = kp 37 _, err := ts.KeyStore.Store(kp) 38 require.NoError(t, err) 39 } 40 41 func makeNonManaged(t *testing.T, ts *TestStore, opName string, kp nkeys.KeyPair) { 42 t.Helper() 43 storeFile := filepath.Join(ts.Dir, "store", opName, ".nsc") 44 require.FileExists(t, storeFile) 45 d, err := Read(storeFile) 46 require.NoError(t, err) 47 var info store.Info 48 json.Unmarshal(d, &info) 49 require.True(t, info.Managed) 50 info.Managed = false 51 require.Equal(t, opName, info.Name) 52 err = WriteJson(storeFile, info) 53 require.NoError(t, err) 54 storeOperatorKey(t, ts, kp) 55 } 56 57 func checkJwtVersion(t *testing.T, ts *TestStore, opName string, version int, token string) { 58 t.Helper() 59 target := filepath.Join(ts.Dir, "store", opName, fmt.Sprintf("%s.jwt", opName)) 60 require.FileExists(t, target) 61 d, err := Read(target) 62 require.NoError(t, err) 63 oc, err := jwt.DecodeOperatorClaims(string(d)) 64 require.NoError(t, err) 65 require.Equal(t, oc.Name, opName) 66 require.Equal(t, oc.Version, version) 67 if token != "" { 68 require.Equal(t, string(d), token) 69 } 70 } 71 72 func executeFailingCmd(t *testing.T, args ...string) { 73 t.Helper() 74 _, _, err := ExecuteCmd(rootCmd, args...) // could be any command 75 require.Error(t, err) 76 require.Contains(t, err.Error(), "This version of nsc only supports jwtV2") 77 require.Contains(t, err.Error(), "upgrade-jwt") 78 } 79 80 func executePassingCmd(t *testing.T, args ...string) { 81 t.Helper() 82 _, _, err := ExecuteCmd(rootCmd, args...) // could be any command 83 require.NoError(t, err) 84 } 85 86 func createOperator(t *testing.T, tempDir string, opName string) (fileV1 string, tokenV1 string, fileV2 string, tokenV2 string, kp nkeys.KeyPair, pub string) { 87 var err error 88 _, pub, kp = CreateOperatorKey(t) 89 90 ocV1 := jwtv1.NewOperatorClaims(pub) 91 ocV1.Name = opName 92 tokenV1, err = ocV1.Encode(kp) 93 require.NoError(t, err) 94 95 ocV2 := jwt.NewOperatorClaims(pub) 96 ocV2.Name = opName 97 tokenV2, err = ocV2.Encode(kp) 98 require.NoError(t, err) 99 100 fileV1 = filepath.Join(tempDir, fmt.Sprintf("%s.v1.jwt", opName)) 101 err = Write(fileV1, []byte(tokenV1)) 102 require.NoError(t, err) 103 104 fileV2 = filepath.Join(tempDir, fmt.Sprintf("%s.v2.jwt", opName)) 105 err = Write(fileV2, []byte(tokenV2)) 106 require.NoError(t, err) 107 require.NotEqual(t, tokenV1, tokenV2) 108 return 109 } 110 111 func TestUpgradeNonManaged(t *testing.T) { 112 tempDir, err := ioutil.TempDir("", "") 113 require.NoError(t, err) 114 defer os.RemoveAll(tempDir) 115 _, token, _, _, kp, _ := createOperator(t, tempDir, "O") 116 ts := NewTestStoreWithOperatorJWT(t, token) 117 defer ts.Done(t) 118 makeNonManaged(t, ts, "O", kp) 119 checkJwtVersion(t, ts, "O", 1, token) 120 executeFailingCmd(t, "list", "keys") // could be any command 121 executeFailingCmd(t, "edit", "operator", "--tag", "foo") // try writing operator 122 executePassingCmd(t, "env") // only few exceptions 123 124 _, _, err = ExecuteInteractiveCmd(rootCmd, []interface{}{false, false}, "upgrade-jwt") // only works in interactive mode 125 require.NoError(t, err) 126 checkJwtVersion(t, ts, "O", 1, token) 127 _, _, err = ExecuteInteractiveCmd(rootCmd, []interface{}{false, true}, "upgrade-jwt") 128 require.NoError(t, err) 129 130 checkJwtVersion(t, ts, "O", 2, "") 131 executePassingCmd(t, "list", "keys") // retry earlier command 132 executePassingCmd(t, "edit", "operator", "--tag", "foo") // try writing operator 133 checkJwtVersion(t, ts, "O", 2, "") 134 } 135 136 func TestUpgradeNoKeyNonManaged(t *testing.T) { 137 tempDir, err := ioutil.TempDir("", "") 138 require.NoError(t, err) 139 defer os.RemoveAll(tempDir) 140 _, token, _, _, kp, pub := createOperator(t, tempDir, "O") 141 ts := NewTestStoreWithOperatorJWT(t, token) 142 defer ts.Done(t) 143 makeNonManaged(t, ts, "O", kp) 144 err = ts.KeyStore.Remove(pub) 145 require.NoError(t, err) 146 checkJwtVersion(t, ts, "O", 1, token) 147 executeFailingCmd(t, "list", "keys") // could be any command 148 executeFailingCmd(t, "edit", "operator", "--tag", "foo") // try writing operator 149 executePassingCmd(t, "env") // only few exceptions 150 151 _, stdErr, err := ExecuteInteractiveCmd(rootCmd, []interface{}{}, "upgrade-jwt") // only works in interactive mode 152 require.NoError(t, err) 153 require.Contains(t, stdErr, "Identity Key for Operator") 154 require.Contains(t, stdErr, "you need to restore it for this command to work") 155 checkJwtVersion(t, ts, "O", 1, token) 156 storeOperatorKey(t, ts, kp) 157 _, _, err = ExecuteInteractiveCmd(rootCmd, []interface{}{false, true}, "upgrade-jwt") 158 require.NoError(t, err) 159 160 checkJwtVersion(t, ts, "O", 2, "") 161 executePassingCmd(t, "list", "keys") // retry earlier command 162 executePassingCmd(t, "edit", "operator", "--tag", "foo") // try writing operator 163 checkJwtVersion(t, ts, "O", 2, "") 164 } 165 166 func TestUpgradeManaged(t *testing.T) { 167 tempDir, err := ioutil.TempDir("", "") 168 require.NoError(t, err) 169 defer os.RemoveAll(tempDir) 170 _, tokenV1, tfV2, tokenV2, _, _ := createOperator(t, tempDir, "O") 171 ts := NewTestStoreWithOperatorJWT(t, tokenV1) 172 defer ts.Done(t) 173 checkJwtVersion(t, ts, "O", 1, tokenV1) 174 executeFailingCmd(t, "list", "keys") // could be any command 175 executePassingCmd(t, "env") // only few exceptions 176 177 _, stdErr, err := ExecuteInteractiveCmd(rootCmd, []interface{}{false}, "upgrade-jwt") // only works in interactive mode 178 require.NoError(t, err) 179 require.Contains(t, stdErr, "Your store is in managed mode") 180 require.Contains(t, stdErr, "nsc add operator --force --url") 181 checkJwtVersion(t, ts, "O", 1, tokenV1) // assert nothing was changed 182 183 executePassingCmd(t, "add", "operator", "--force", "--url", tfV2) 184 checkJwtVersion(t, ts, "O", 2, tokenV2) // assert nothing was changed 185 executePassingCmd(t, "list", "keys") // retry earlier command 186 } 187 188 func TestUpgradeBackup(t *testing.T) { 189 tempDir, err := ioutil.TempDir("", "") 190 require.NoError(t, err) 191 defer os.RemoveAll(tempDir) 192 _, token, _, _, kp, _ := createOperator(t, tempDir, "O") 193 ts := NewTestStoreWithOperatorJWT(t, token) 194 defer ts.Done(t) 195 makeNonManaged(t, ts, "O", kp) 196 checkJwtVersion(t, ts, "O", 1, token) 197 backup := filepath.Join(ts.Dir, "test.zip") 198 _, _, err = ExecuteInteractiveCmd(rootCmd, []interface{}{true, backup, false}, "upgrade-jwt") // only works in interactive mode 199 require.NoError(t, err) 200 closer, err := zip.OpenReader(backup) 201 require.NoError(t, err) 202 defer closer.Close() 203 require.NoError(t, err) 204 require.Len(t, closer.File, 2) // .nsc and O.jwt 205 }