github.com/kbehouse/nsc@v0.0.6/cmd/editoperator_test.go (about) 1 /* 2 * 3 * * Copyright 2018-2019 The NATS Authors 4 * * Licensed under the Apache License, Version 2.0 (the "License"); 5 * * you may not use this file except in compliance with the License. 6 * * You may obtain a copy of the License at 7 * * 8 * * http://www.apache.org/licenses/LICENSE-2.0 9 * * 10 * * Unless required by applicable law or agreed to in writing, software 11 * * distributed under the License is distributed on an "AS IS" BASIS, 12 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * * See the License for the specific language governing permissions and 14 * * limitations under the License. 15 * 16 */ 17 18 package cmd 19 20 import ( 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strings" 26 "testing" 27 28 "github.com/nats-io/nkeys" 29 30 "github.com/kbehouse/nsc/cmd/store" 31 "github.com/nats-io/jwt/v2" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func Test_EditOperator(t *testing.T) { 36 ts := NewTestStore(t, "O") 37 defer ts.Done(t) 38 ts.AddAccount(t, "TEST") 39 40 tests := CmdTests{ 41 {CreateEditOperatorCmd(), []string{"edit", "operator"}, nil, []string{"specify an edit option"}, true}, 42 {CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "ABTFVAXATJEOKIBESJ3LO3JTAAMDCZ755DLAAGGSDMH5TU6HSFL7YNYY"}, nil, []string{"set system account"}, false}, 43 {CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "TEST"}, nil, []string{"set system account"}, false}, 44 {CreateEditOperatorCmd(), []string{"edit", "operator", "--system-account", "DOESNOTEXIST"}, nil, []string{"account DOESNOTEXIST does not exist in the current operator"}, true}, 45 {CreateEditOperatorCmd(), []string{"edit", "operator", "--sk"}, nil, []string{"flag needs an argument"}, true}, 46 {CreateEditOperatorCmd(), []string{"edit", "operator", "--sk", "SAADOZRUTPZS6LIXS6CSSSW5GXY3DNMQMSDTVWHQNHQTIBPGNSADSMBPEU"}, nil, []string{"invalid operator signing key"}, true}, 47 {CreateEditOperatorCmd(), []string{"edit", "operator", "--sk", "OBMWGGURAFWMH3AFDX65TVIH4ZYSL7UKZ3LOH2ZRWIAU7PGZ3IJNR6W5"}, nil, []string{"edited operator"}, false}, 48 {CreateEditOperatorCmd(), []string{"edit", "operator", "--tag", "O", "--start", "2019-04-13", "--expiry", "2050-01-01"}, nil, []string{"edited operator"}, false}, 49 {CreateEditOperatorCmd(), []string{"edit", "operator", "--require-signing-keys"}, nil, []string{"needs to be issued with a signing key first"}, true}, 50 } 51 52 tests.Run(t, "root", "edit") 53 } 54 55 func readJWT(t *testing.T, elem ...string) string { 56 t.Helper() 57 fp := filepath.Join(elem...) 58 require.FileExists(t, fp) 59 theJWT, err := ioutil.ReadFile(fp) 60 require.NoError(t, err) 61 return string(theJWT) 62 } 63 64 func checkAcc(t *testing.T, ts *TestStore, acc string) { 65 t.Helper() 66 opJWT := readJWT(t, ts.Dir, "store", "O", "O.jwt") 67 op, err := jwt.DecodeOperatorClaims(opJWT) 68 require.NoError(t, err) 69 require.True(t, op.StrictSigningKeyUsage) 70 accJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, fmt.Sprintf("%s.jwt", acc)) 71 ac, err := jwt.DecodeAccountClaims(accJWT) 72 require.NoError(t, err) 73 require.NotEqual(t, ac.Issuer, op.Subject) 74 require.Equal(t, ac.Issuer, op.SigningKeys[0]) 75 _, _, err = ExecuteCmd(createValidateCommand(), "--all-accounts") 76 require.NoError(t, err) 77 } 78 79 func checkUsr(t *testing.T, ts *TestStore, acc string) { 80 t.Helper() 81 opJWT := readJWT(t, ts.Dir, "store", "O", "O.jwt") 82 op, err := jwt.DecodeOperatorClaims(opJWT) 83 require.NoError(t, err) 84 require.True(t, op.StrictSigningKeyUsage) 85 accJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, fmt.Sprintf("%s.jwt", acc)) 86 ac, err := jwt.DecodeAccountClaims(accJWT) 87 require.NoError(t, err) 88 require.NotEqual(t, ac.Issuer, op.Subject) 89 require.Equal(t, ac.Issuer, op.SigningKeys[0]) 90 usrJWT := readJWT(t, ts.Dir, "store", "O", "accounts", acc, "users", "U.jwt") 91 uc, err := jwt.DecodeUserClaims(usrJWT) 92 require.NoError(t, err) 93 require.NotEqual(t, uc.Issuer, ac.Subject) 94 require.Equal(t, uc.IssuerAccount, ac.Subject) 95 require.Equal(t, uc.Issuer, ac.SigningKeys.Keys()[0]) 96 } 97 98 func Test_EditOperatorRequireSigningKeys(t *testing.T) { 99 ts := NewEmptyStore(t) 100 101 _, err := os.Lstat(filepath.Join(ts.Dir, "store")) 102 if err != nil && !os.IsNotExist(err) { 103 t.Fatal(err) 104 } 105 // Perform all operations that would end up signing account/user/activation jwt 106 _, _, err = ExecuteCmd(CreateAddOperatorCmd(), "--name", "O") 107 require.NoError(t, err) 108 _, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--sk", "generate") 109 require.NoError(t, err) 110 _, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--require-signing-keys") 111 require.NoError(t, err) 112 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "EXPORTER") 113 require.NoError(t, err) 114 checkAcc(t, ts, "EXPORTER") 115 _, _, err = ExecuteCmd(createEditAccount(), "--name", "EXPORTER", "--sk", "generate") 116 require.NoError(t, err) 117 checkAcc(t, ts, "EXPORTER") 118 _, _, err = ExecuteCmd(createAddExportCmd(), "--subject", "sub.public") 119 require.NoError(t, err) 120 checkAcc(t, ts, "EXPORTER") 121 _, _, err = ExecuteCmd(createAddExportCmd(), "--subject", "sub.private", "--private") 122 require.NoError(t, err) 123 checkAcc(t, ts, "EXPORTER") 124 _, _, err = ExecuteCmd(createEditExportCmd(), "--account", "EXPORTER", "--subject", "sub.public", "--description", "foo") 125 require.NoError(t, err) 126 checkAcc(t, ts, "EXPORTER") 127 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "A") 128 require.NoError(t, err) 129 checkAcc(t, ts, "A") 130 _, _, err = ExecuteCmd(createEditAccount(), "--name", "A", "--sk", "generate") 131 require.NoError(t, err) 132 checkAcc(t, ts, "A") 133 aAc, err := jwt.DecodeAccountClaims(readJWT(t, ts.Dir, "store", "O", "accounts", "A", "A.jwt")) 134 require.NoError(t, err) 135 expAc, err := jwt.DecodeAccountClaims(readJWT(t, ts.Dir, "store", "O", "accounts", "EXPORTER", "EXPORTER.jwt")) 136 require.NoError(t, err) 137 outpath := filepath.Join(ts.Dir, "token.jwt") 138 _, _, err = ExecuteCmd(createGenerateActivationCmd(), "--account", "EXPORTER", "--subject", "sub.private", 139 "--target-account", aAc.Subject, "--output-file", outpath) 140 require.NoError(t, err) 141 act, err := jwt.DecodeActivationClaims(strings.Split(readJWT(t, outpath), "\n")[1]) // strip decoration 142 require.NoError(t, err) 143 require.NotEqual(t, act.Issuer, act.IssuerAccount) 144 require.Equal(t, act.IssuerAccount, expAc.Subject) 145 require.Equal(t, act.Issuer, expAc.SigningKeys.Keys()[0]) 146 _, _, err = ExecuteCmd(createAddImportCmd(), "--account", "A", "--token", outpath) 147 require.NoError(t, err) 148 checkAcc(t, ts, "A") 149 _, _, err = ExecuteCmd(createAddImportCmd(), "--account", "A", "--src-account", expAc.Subject, 150 "--remote-subject", "sub.public") 151 require.NoError(t, err) 152 checkAcc(t, ts, "A") 153 _, _, err = ExecuteCmd(createDeleteImportCmd(), "--account", "A", "--subject", "sub.public") 154 require.NoError(t, err) 155 checkAcc(t, ts, "A") 156 _, _, err = ExecuteCmd(createDeleteExportCmd(), "--account", "EXPORTER", "--subject", "sub.public") 157 require.NoError(t, err) 158 checkAcc(t, ts, "EXPORTER") 159 _, _, err = ExecuteCmd(CreateAddUserCmd(), "--account", "A", "--name", "U") 160 require.NoError(t, err) 161 checkUsr(t, ts, "A") 162 _, _, err = ExecuteCmd(CreateEditUserCmd(), "--account", "A", "--name", "U", "--tag", "foo") 163 require.NoError(t, err) 164 checkUsr(t, ts, "A") 165 _, _, err = ExecuteCmd(CreateDeleteUserCmd(), "--account", "A", "--name", "U", "--revoke") 166 require.NoError(t, err) 167 checkAcc(t, ts, "A") 168 uk, err := nkeys.CreateUser() 169 require.NoError(t, err) 170 pubUk, err := uk.PublicKey() 171 require.NoError(t, err) 172 _, _, err = ExecuteCmd(CreateRevokeUserCmd(), "--account", "A", "--user-public-key", pubUk) 173 require.NoError(t, err) 174 checkAcc(t, ts, "A") 175 } 176 177 func Test_EditOperatorRequireSigningKeysManaged(t *testing.T) { 178 ts := NewEmptyStore(t) 179 defer ts.Done(t) 180 _, err := os.Lstat(filepath.Join(ts.Dir, "store")) 181 if err != nil && !os.IsNotExist(err) { 182 t.Fatal(err) 183 } 184 _, pub, kp := CreateOperatorKey(t) 185 oc := jwt.NewOperatorClaims(pub) 186 oc.Name = "O" 187 oc.StrictSigningKeyUsage = true 188 _, psk, sk := CreateOperatorKey(t) 189 ts.KeyStore.Store(sk) 190 oc.SigningKeys.Add(psk) 191 token, err := oc.Encode(kp) 192 require.NoError(t, err) 193 tf := filepath.Join(ts.Dir, "O.jwt") 194 err = Write(tf, []byte(token)) 195 require.NoError(t, err) 196 _, _, err = ExecuteCmd(CreateAddOperatorCmd(), "--url", tf) // causes a managed store 197 require.NoError(t, err) 198 // perform operations in a managed store and assure identity is not used 199 _, _, err = ExecuteCmd(CreateAddAccountCmd(), "--name", "A") 200 require.NoError(t, err) 201 checkAcc(t, ts, "A") 202 _, _, err = ExecuteCmd(createEditAccount(), "--name", "A", "--sk", "generate") 203 require.NoError(t, err) 204 checkAcc(t, ts, "A") 205 _, _, err = ExecuteCmd(CreateAddUserCmd(), "--account", "A", "--name", "U") 206 require.NoError(t, err) 207 checkUsr(t, ts, "A") 208 _, _, err = ExecuteCmd(CreateEditUserCmd(), "--account", "A", "--name", "U", "--tag", "foo") 209 require.NoError(t, err) 210 checkUsr(t, ts, "A") 211 } 212 213 func Test_EditOperatorSigningKeys(t *testing.T) { 214 ts := NewTestStore(t, "O") 215 defer ts.Done(t) 216 217 s1, pk1, _ := CreateOperatorKey(t) 218 _, pk2, _ := CreateOperatorKey(t) 219 220 _, _, err := ExecuteCmd(CreateEditOperatorCmd(), "--sk", pk1, "--sk", pk2, "--sk", "generate") 221 require.NoError(t, err) 222 223 d, err := ts.Store.Read(store.JwtName("O")) 224 require.NoError(t, err) 225 226 oc, err := jwt.DecodeOperatorClaims(string(d)) 227 require.NoError(t, err) 228 229 require.Contains(t, oc.SigningKeys, pk1) 230 require.Contains(t, oc.SigningKeys, pk2) 231 require.Len(t, oc.SigningKeys, 3) 232 233 _, _, err = ExecuteCmd(HoistRootFlags(CreateAddAccountCmd()), "--name", "A", "-K", string(s1)) 234 require.NoError(t, err) 235 236 ac, err := ts.Store.ReadAccountClaim("A") 237 require.NoError(t, err) 238 require.True(t, oc.DidSign(ac)) 239 240 _, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--rm-sk", pk1) 241 require.NoError(t, err) 242 243 d, err = ts.Store.Read(store.JwtName("O")) 244 require.NoError(t, err) 245 246 oc, err = jwt.DecodeOperatorClaims(string(d)) 247 require.NoError(t, err) 248 249 require.NotContains(t, oc.SigningKeys, pk1) 250 require.Contains(t, oc.SigningKeys, pk2) 251 require.False(t, oc.DidSign(ac)) 252 } 253 254 func Test_EditOperatorServiceURLs(t *testing.T) { 255 ts := NewTestStore(t, "O") 256 defer ts.Done(t) 257 258 u1 := "nats://localhost:4222" 259 u2 := "tls://localhost:4333" 260 oc, err := ts.Store.ReadOperatorClaim() 261 require.NoError(t, err) 262 require.Len(t, oc.OperatorServiceURLs, 0) 263 264 _, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--service-url", u1, "--service-url", u2) 265 require.NoError(t, err) 266 267 oc, err = ts.Store.ReadOperatorClaim() 268 require.NoError(t, err) 269 require.Contains(t, oc.OperatorServiceURLs, u1) 270 require.Contains(t, oc.OperatorServiceURLs, u2) 271 272 _, _, err = ExecuteCmd(CreateEditOperatorCmd(), "--rm-service-url", u1) 273 require.NoError(t, err) 274 oc, err = ts.Store.ReadOperatorClaim() 275 require.NoError(t, err) 276 require.NotContains(t, oc.OperatorServiceURLs, u1) 277 require.Contains(t, oc.OperatorServiceURLs, u2) 278 } 279 280 func Test_EditOperatorServiceURLsInteractive(t *testing.T) { 281 ts := NewTestStore(t, "O") 282 defer ts.Done(t) 283 ts.AddAccount(t, "SYS") 284 pub := ts.GetAccountPublicKey(t, "SYS") 285 286 u1 := "nats://localhost:4222" 287 u2 := "tls://localhost:4333" 288 as := "nats://localhost:4222" 289 290 // valid from, valid until, add tags, acc jwt server, add service url, url, add another, url, add another, 291 // system account (defaults to SYS), add signing key 292 inputs := []interface{}{"0", "0", true, "xxx", false, as, true, u1, true, u2, false, true, false, false} 293 294 _, _, err := ExecuteInteractiveCmd(CreateEditOperatorCmd(), inputs) 295 require.NoError(t, err) 296 297 oc, err := ts.Store.ReadOperatorClaim() 298 require.NoError(t, err) 299 require.Contains(t, oc.OperatorServiceURLs, u1) 300 require.Contains(t, oc.OperatorServiceURLs, u2) 301 require.Contains(t, oc.Tags, "xxx") 302 require.Equal(t, oc.AccountServerURL, as) 303 require.Equal(t, oc.SystemAccount, pub) 304 305 // valid from, valid until, acc jwt server, add service url, remove server urls, add signing key 306 inputs = []interface{}{"0", "0", true, []int{0}, false, "", false, true, []int{0}, false, false} 307 308 _, _, err = ExecuteInteractiveCmd(CreateEditOperatorCmd(), inputs) 309 require.NoError(t, err) 310 oc, err = ts.Store.ReadOperatorClaim() 311 require.NoError(t, err) 312 require.NotContains(t, oc.OperatorServiceURLs, u1) 313 require.Contains(t, oc.OperatorServiceURLs, u2) 314 require.NotContains(t, oc.Tags, "xxx") 315 require.Equal(t, oc.AccountServerURL, "") 316 require.Equal(t, oc.SystemAccount, pub) 317 }