github.com/nats-io/nsc@v0.0.0-20221206222106-35db9400b257/cmd/push_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 "os" 22 "path/filepath" 23 "regexp" 24 "runtime" 25 "strings" 26 "testing" 27 28 "github.com/nats-io/jwt/v2" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func Test_SyncOK(t *testing.T) { 33 _, _, okp := CreateOperatorKey(t) 34 as, m := RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2}) 35 defer as.Close() 36 37 ts := NewTestStoreWithOperator(t, "T", okp) 38 err := ts.Store.StoreRaw(m["operator"]) 39 require.NoError(t, err) 40 ts.AddAccount(t, "A") 41 42 // edit the jwt 43 _, _, err = ExecuteCmd(createEditAccount(), "--tag", "A") 44 require.NoError(t, err) 45 46 // sync the store 47 _, _, err = ExecuteCmd(createPushCmd(), "--account", "A") 48 require.NoError(t, err) 49 50 // verify the tag was stored 51 ac, err := ts.Store.ReadAccountClaim("A") 52 require.NoError(t, err) 53 require.Contains(t, ac.Tags, "a") 54 } 55 56 func Test_SyncNoURL(t *testing.T) { 57 _, _, okp := CreateOperatorKey(t) 58 as, m := RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2}) 59 ts := NewTestStoreWithOperatorJWT(t, string(m["operator"])) 60 ts.AddAccount(t, "A") 61 as.Close() 62 63 // remove the account server so we cannot push 64 oc, err := ts.Store.ReadOperatorClaim() 65 require.NoError(t, err) 66 oc.AccountServerURL = "" 67 token, err := oc.Encode(okp) 68 require.NoError(t, err) 69 ts.Store.StoreClaim([]byte(token)) 70 71 _, _, err = ExecuteCmd(createPushCmd(), "--account", "A") 72 require.Error(t, err) 73 require.Contains(t, err.Error(), "no account server url or nats-server url was provided by the operator jwt") 74 } 75 76 func Test_SyncNoServer(t *testing.T) { 77 as, m := RunTestAccountServer(t) 78 ts := NewTestStoreWithOperatorJWT(t, string(m["operator"])) 79 ts.AddAccount(t, "A") 80 as.Close() 81 82 _, stderr, err := ExecuteCmd(createPushCmd(), "--account", "A") 83 require.Error(t, err) 84 if runtime.GOOS == "windows" { 85 require.Contains(t, stderr, "connectex: No connection") 86 } else { 87 require.Contains(t, stderr, "connect: connection refused") 88 } 89 } 90 91 func Test_SyncManaged(t *testing.T) { 92 as, m := RunTestAccountServer(t) 93 defer as.Close() 94 95 ts := NewTestStoreWithOperatorJWT(t, string(m["operator"])) 96 defer ts.Done(t) 97 98 ts.AddAccount(t, "A") 99 ac, err := ts.Store.ReadAccountClaim("A") 100 require.NoError(t, err) 101 require.False(t, ac.IsSelfSigned()) 102 } 103 104 func Test_SyncManualServer(t *testing.T) { 105 _, _, okp := CreateOperatorKey(t) 106 as, m := RunTestAccountServerWithOperatorKP(t, okp, TasOpts{Vers: 2}) 107 defer as.Close() 108 109 // remove the account server 110 op, err := jwt.DecodeOperatorClaims(string(m["operator"])) 111 require.NoError(t, err) 112 op.AccountServerURL = "" 113 s, err := op.Encode(okp) 114 require.NoError(t, err) 115 m["operator"] = []byte(s) 116 117 ts := NewTestStoreWithOperator(t, "T", okp) 118 err = ts.Store.StoreRaw(m["operator"]) 119 require.NoError(t, err) 120 ts.AddAccount(t, "A") 121 122 // edit the jwt 123 _, _, err = ExecuteCmd(createEditAccount(), "--tag", "A") 124 require.NoError(t, err) 125 126 // sync the store 127 _, _, err = ExecuteCmd(createPushCmd(), "--account", "A", "--account-jwt-server-url", as.URL) 128 require.NoError(t, err) 129 130 // verify the tag was stored 131 ac, err := ts.Store.ReadAccountClaim("A") 132 require.NoError(t, err) 133 require.Contains(t, ac.Tags, "a") 134 } 135 136 func deleteSetup(t *testing.T, del bool) (string, []string, *TestStore) { 137 t.Helper() 138 ts := NewEmptyStore(t) 139 _, _, err := ExecuteCmd(createAddOperatorCmd(), "--name", "OP", "--sys") 140 require.NoError(t, err) 141 serverconf := filepath.Join(ts.Dir, "server.conf") 142 _, _, err = ExecuteCmd(createServerConfigCmd(), "--nats-resolver", "--config-file", serverconf) 143 require.NoError(t, err) 144 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC1") 145 require.NoError(t, err) 146 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC2") 147 require.NoError(t, err) 148 // modify the generated file so testing becomes easier by knowing where the jwt directory is 149 data, err := os.ReadFile(serverconf) 150 require.NoError(t, err) 151 dir, err := os.MkdirTemp("", "Test_SyncNatsResolver-jwt-") 152 require.NoError(t, err) 153 data = bytes.ReplaceAll(data, []byte(`dir: './jwt'`), []byte(fmt.Sprintf(`dir: '%s'`, dir))) 154 data = bytes.ReplaceAll(data, []byte(`dir: '.\jwt'`), []byte(fmt.Sprintf(`dir: '%s'`, dir))) 155 data = bytes.ReplaceAll(data, []byte(`allow_delete: false`), []byte(fmt.Sprintf(`allow_delete: %t`, del))) 156 err = os.WriteFile(serverconf, data, 0660) 157 require.NoError(t, err) 158 ports := ts.RunServerWithConfig(t, serverconf) 159 require.NotNil(t, ports) 160 // only after server start as ports are not yet known in tests 161 _, _, err = ExecuteCmd(createEditOperatorCmd(), "--account-jwt-server-url", ports.Nats[0]) 162 require.NoError(t, err) 163 _, _, err = ExecuteCmd(createPushCmd(), "--all") 164 require.NoError(t, err) 165 // test to assure AC1/AC2/SYS where pushed 166 filesPre, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 167 require.NoError(t, err) 168 require.Equal(t, len(filesPre), 3) 169 _, _, err = ExecuteCmd(createDeleteAccountCmd(), "--name", "AC2") 170 require.NoError(t, err) 171 // exists as nsc has a bad default account now (is not pushed, hence not in file counts) 172 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC3") 173 require.NoError(t, err) 174 return dir, filesPre, ts 175 } 176 177 func Test_SyncNatsResolverDelete(t *testing.T) { 178 dir, filesPre, ts := deleteSetup(t, true) 179 defer os.Remove(dir) 180 defer ts.Done(t) 181 _, _, err := ExecuteCmd(createPushCmd(), "--prune", "--system-account", "SYS", "--system-user", "sys") 182 require.NoError(t, err) 183 // test to assure AC1/SYS where pushed/pruned 184 filesPost, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 185 require.NoError(t, err) 186 require.Equal(t, 2, len(filesPost)) 187 // assert only AC1/SYS overlap in pre/post 188 sameCnt := 0 189 for _, f1 := range filesPost { 190 for _, f2 := range filesPre { 191 if f1 == f2 { 192 sameCnt++ 193 break 194 } 195 } 196 } 197 require.Equal(t, 2, sameCnt) 198 filesDeleted, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt.deleted") 199 require.NoError(t, err) 200 require.Equal(t, 1, len(filesDeleted)) 201 } 202 203 func Test_SyncNatsResolverExplicitDelete(t *testing.T) { 204 dir, filesPre, ts := deleteSetup(t, true) 205 defer os.Remove(dir) 206 defer ts.Done(t) 207 _, _, err := ExecuteCmd(createPushCmd(), "--account-removal", "AC1") 208 require.NoError(t, err) 209 // test to assure AC1/SYS where pushed/pruned 210 filesPost, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 211 require.NoError(t, err) 212 require.Equal(t, 2, len(filesPost)) 213 // assert only AC1/SYS overlap in pre/post 214 sameCnt := 0 215 for _, f1 := range filesPost { 216 for _, f2 := range filesPre { 217 if f1 == f2 { 218 sameCnt++ 219 break 220 } 221 } 222 } 223 require.Equal(t, 2, sameCnt) 224 filesDeleted, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt.deleted") 225 require.NoError(t, err) 226 require.Equal(t, 1, len(filesDeleted)) 227 } 228 229 func Test_SyncNatsResolverDiff(t *testing.T) { 230 dir, _, ts := deleteSetup(t, true) 231 defer os.Remove(dir) 232 defer ts.Done(t) 233 _, stdErr, err := ExecuteCmd(createPushCmd(), "--diff") 234 require.NoError(t, err) 235 require.Contains(t, stdErr, "only exists in server") 236 require.Contains(t, stdErr, "named AC1 exists") 237 require.Contains(t, stdErr, "named SYS exists") 238 239 re := regexp.MustCompile("[A-Z0-9]* named AC1 exists") 240 line := re.FindString(stdErr) 241 accId := strings.TrimSuffix(line, " named AC1 exists") 242 243 _, _, err = ExecuteCmd(createPushCmd(), "--account-removal", accId) 244 require.NoError(t, err) 245 filesDeleted, err := filepath.Glob(dir + string(os.PathSeparator) + accId + ".jwt.deleted") 246 require.NoError(t, err) 247 require.Equal(t, 1, len(filesDeleted)) 248 _, stdErr, err = ExecuteCmd(createPushCmd(), "--diff") 249 require.NoError(t, err) 250 require.Contains(t, stdErr, "only exists in server") 251 require.NotContains(t, stdErr, "named AC1 exists") 252 require.Contains(t, stdErr, "named SYS exists") 253 } 254 255 func Test_SyncNatsResolverDeleteSYS(t *testing.T) { 256 dir, filesPre, ts := deleteSetup(t, true) 257 defer os.Remove(dir) 258 defer ts.Done(t) 259 _, _, err := ExecuteCmd(createDeleteAccountCmd(), "--name", "SYS") 260 require.NoError(t, err) 261 // exists as nsc has a bad default account now (is not pushed, hence not in file counts) 262 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC4") 263 require.NoError(t, err) 264 _, _, err = ExecuteCmd(createPushCmd(), "--prune") // will fail as system acc can't be deleted 265 require.Error(t, err) // this will actually not hit the server as the system account is already deleted 266 filesPost, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 267 require.NoError(t, err) 268 require.Equal(t, 3, len(filesPost)) 269 require.Equal(t, filesPre, filesPost) 270 } 271 272 func Test_SyncNatsResolverNoDelete(t *testing.T) { 273 dir, filesPre, ts := deleteSetup(t, false) 274 defer os.Remove(dir) 275 defer ts.Done(t) 276 _, _, err := ExecuteCmd(createPushCmd(), "--prune") 277 require.Error(t, err) 278 // test to assure that pruning did not happen 279 filesPost, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 280 require.NoError(t, err) 281 require.Equal(t, 3, len(filesPost)) 282 require.Equal(t, filesPre, filesPost) 283 } 284 285 func Test_SyncBadUrl(t *testing.T) { 286 ts := NewEmptyStore(t) 287 defer ts.Done(t) 288 _, _, err := ExecuteCmd(createAddOperatorCmd(), "--name", "OP", "--sys") 289 require.NoError(t, err) 290 serverconf := filepath.Join(ts.Dir, "server.conf") 291 _, _, err = ExecuteCmd(createServerConfigCmd(), "--nats-resolver", "--config-file", serverconf) 292 require.NoError(t, err) 293 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "AC1") 294 require.NoError(t, err) 295 // modify the generated file so testing becomes easier by knowing where the jwt directory is 296 data, err := os.ReadFile(serverconf) 297 require.NoError(t, err) 298 dir, err := os.MkdirTemp("", "Test_SyncNatsResolver-jwt-") 299 require.NoError(t, err) 300 defer os.Remove(dir) 301 data = bytes.ReplaceAll(data, []byte(`dir: './jwt'`), []byte(fmt.Sprintf(`dir: '%s'`, dir))) 302 err = os.WriteFile(serverconf, data, 0660) 303 require.NoError(t, err) 304 ports := ts.RunServerWithConfig(t, serverconf) 305 require.NotNil(t, ports) 306 // deliberately test if http push to a nats server kills it or not 307 badUrl := strings.ReplaceAll(ports.Nats[0], "nats://", "http://") 308 _, _, err = ExecuteCmd(createEditOperatorCmd(), "--account-jwt-server-url", badUrl) 309 require.NoError(t, err) 310 _, errOut, err := ExecuteCmd(createPushCmd(), "--all") 311 require.Error(t, err) 312 require.Contains(t, errOut, `Post "`+badUrl) 313 // Fix bad url 314 _, _, err = ExecuteCmd(createEditOperatorCmd(), "--account-jwt-server-url", ports.Nats[0]) 315 require.NoError(t, err) 316 // Try again, thus also testing if the server is still around 317 // Provide explicit system account user to connect 318 _, _, err = ExecuteCmd(createPushCmd(), "--all", "--system-account", "SYS", "--system-user", "sys") 319 require.NoError(t, err) 320 // test to assure AC1/AC2/SYS where pushed 321 filesPre, err := filepath.Glob(dir + string(os.PathSeparator) + "*.jwt") 322 require.NoError(t, err) 323 require.Equal(t, len(filesPre), 2) 324 }