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