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