diff --git a/cmd/editaccount.go b/cmd/editaccount.go index 08ecf7ae..2d206ca4 100644 --- a/cmd/editaccount.go +++ b/cmd/editaccount.go @@ -57,6 +57,7 @@ func createEditAccount() *cobra.Command { return RunAction(cmd, args, params) }, } + cmd.Flags().BoolVarP(¶ms.caseSensitiveTags, "case-sensitive-tags", "", false, "allow tags values that are not lowercase (false)") cmd.Flags().StringSliceVarP(¶ms.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times") cmd.Flags().StringSliceVarP(¶ms.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times") params.conns = -1 diff --git a/cmd/editaccount_test.go b/cmd/editaccount_test.go index 5bb84647..0f7a8660 100644 --- a/cmd/editaccount_test.go +++ b/cmd/editaccount_test.go @@ -524,3 +524,22 @@ func Test_EnableTierNoOtherFlag(t *testing.T) { require.Error(t, err) require.Equal(t, "rm-js-tier is exclusive of all other js options", err.Error()) } + +func Test_EditAccountCaseSensitiveTags(t *testing.T) { + ts := NewTestStore(t, "O") + defer ts.Done(t) + ts.AddAccount(t, "A") + + _, _, err := ExecuteCmd(createEditAccount(), "A", "--case-sensitive-tags", "--tag", "One,Two,three") + require.Nil(t, err) + + _, _, err = ExecuteCmd(createEditAccount(), "A", "--case-sensitive-tags", "--rm-tag", "Three") + require.Error(t, err) + + _, _, err = ExecuteCmd(createEditAccount(), "A", "--case-sensitive-tags", "--rm-tag", "three") + require.NoError(t, err) + + ac, err := ts.Store.ReadAccountClaim("A") + require.NoError(t, err) + require.Equal(t, ac.Tags, jwt.TagList{"One", "Two"}) +} diff --git a/cmd/editoperator.go b/cmd/editoperator.go index ab369742..924f72de 100644 --- a/cmd/editoperator.go +++ b/cmd/editoperator.go @@ -40,6 +40,7 @@ func createEditOperatorCmd() *cobra.Command { } params.signingKeys.BindFlags("sk", "", nkeys.PrefixByteOperator, cmd) cmd.Flags().StringSliceVarP(¶ms.rmSigningKeys, "rm-sk", "", nil, "remove signing key - comma separated list or option can be specified multiple times") + cmd.Flags().BoolVarP(¶ms.caseSensitiveTags, "case-sensitive-tags", "", false, "allow tags values that are not lowercase (false)") cmd.Flags().StringSliceVarP(¶ms.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times") cmd.Flags().StringSliceVarP(¶ms.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times") cmd.Flags().StringVarP(¶ms.asu, "account-jwt-server-url", "u", "", "set account jwt server url for nsc sync (only http/https or nats service (nats/tls/ws/wss) urls supported if updating with nsc)") diff --git a/cmd/editoperator_test.go b/cmd/editoperator_test.go index 9cfb35fb..3e2b901f 100644 --- a/cmd/editoperator_test.go +++ b/cmd/editoperator_test.go @@ -420,3 +420,21 @@ func Test_CannotSetRequireSKWithoutSK(t *testing.T) { require.False(t, oc.StrictSigningKeyUsage) require.Empty(t, oc.SigningKeys) } + +func Test_EditOperatorCaseSensitiveTags(t *testing.T) { + ts := NewTestStore(t, "O") + defer ts.Done(t) + + _, _, err := ExecuteCmd(createEditOperatorCmd(), "--case-sensitive-tags", "--tag", "One,Two,three") + require.Nil(t, err) + + _, _, err = ExecuteCmd(createEditOperatorCmd(), "--case-sensitive-tags", "--rm-tag", "Three") + require.Error(t, err) + + _, _, err = ExecuteCmd(createEditOperatorCmd(), "--case-sensitive-tags", "--rm-tag", "three") + require.NoError(t, err) + + ac, err := ts.Store.ReadOperatorClaim() + require.NoError(t, err) + require.Equal(t, ac.Tags, jwt.TagList{"One", "Two"}) +} diff --git a/cmd/edituser.go b/cmd/edituser.go index 0338590b..d570f732 100644 --- a/cmd/edituser.go +++ b/cmd/edituser.go @@ -71,6 +71,7 @@ nsc edit user --name --rm-response-perms return RunAction(cmd, args, ¶ms) }, } + cmd.Flags().BoolVarP(¶ms.caseSensitiveTags, "case-sensitive-tags", "", false, "allow tags values that are not lowercase (false)") cmd.Flags().StringSliceVarP(¶ms.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times") cmd.Flags().StringSliceVarP(¶ms.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times") cmd.Flags().StringVarP(¶ms.name, "name", "n", "", "user name") diff --git a/cmd/edituser_test.go b/cmd/edituser_test.go index 7a4b7067..09c909ed 100644 --- a/cmd/edituser_test.go +++ b/cmd/edituser_test.go @@ -16,11 +16,12 @@ package cmd import ( - "github.com/nats-io/nsc/v2/cmd/store" "strings" "testing" "time" + "github.com/nats-io/nsc/v2/cmd/store" + "github.com/nats-io/nkeys" cli "github.com/nats-io/cliprompts/v2" @@ -595,3 +596,23 @@ func Test_EditUserConnectionDeleteCase(t *testing.T) { require.NoError(t, err) require.Len(t, claim.AllowedConnectionTypes, 0) } + +func Test_EditUserCaseSensitiveTags(t *testing.T) { + ts := NewTestStore(t, "O") + defer ts.Done(t) + ts.AddAccount(t, "A") + ts.AddUser(t, "A", "U") + + _, _, err := ExecuteCmd(createEditUserCmd(), "-a", "A", "U", "--case-sensitive-tags", "--tag", "One,Two,three") + require.Nil(t, err) + + _, _, err = ExecuteCmd(createEditUserCmd(), "-a", "A", "U", "--case-sensitive-tags", "--rm-tag", "Three") + require.Error(t, err) + + _, _, err = ExecuteCmd(createEditUserCmd(), "-a", "A", "U", "--case-sensitive-tags", "--rm-tag", "three") + require.NoError(t, err) + + ac, err := ts.Store.ReadUserClaim("A", "U") + require.NoError(t, err) + require.Equal(t, ac.Tags, jwt.TagList{"One", "Two"}) +} diff --git a/cmd/genericclaimparams.go b/cmd/genericclaimparams.go index d9ed3872..8553680a 100644 --- a/cmd/genericclaimparams.go +++ b/cmd/genericclaimparams.go @@ -30,8 +30,9 @@ import ( // GenericClaimsParams - TimeParams and tags type GenericClaimsParams struct { TimeParams - tags []string - rmTags []string + tags []string + rmTags []string + caseSensitiveTags bool } func (sp *GenericClaimsParams) Edit(current []string) error { @@ -129,6 +130,16 @@ func (sp *GenericClaimsParams) Run(ctx ActionCtx, claim jwt.Claims, r *store.Rep } } + // if not case-sensitive, normalize the adds/removes + if !sp.caseSensitiveTags { + for idx, t := range sp.tags { + sp.tags[idx] = strings.ToLower(t) + } + for idx, t := range sp.rmTags { + sp.rmTags[idx] = strings.ToLower(t) + } + } + var tags *jwt.TagList switch claim.ClaimType() { @@ -143,17 +154,23 @@ func (sp *GenericClaimsParams) Run(ctx ActionCtx, claim jwt.Claims, r *store.Rep default: panic("unhandled claim type") } - - tags.Add(sp.tags...) - tags.Remove(sp.rmTags...) + if !sp.caseSensitiveTags { + tags.Add(sp.tags...) + tags.Remove(sp.rmTags...) + } else { + tags.AddCaseSensitive(sp.tags...) + if err := tags.RemoveCaseSensitive(sp.rmTags...); err != nil { + return err + } + } sort.Strings(*tags) if r != nil { for _, t := range sp.tags { - r.AddOK("added tag %q", strings.ToLower(t)) + r.AddOK("added tag %q", t) } for _, t := range sp.rmTags { - r.AddOK("removed tag %q", strings.ToLower(t)) + r.AddOK("removed tag %q", t) } } return nil diff --git a/go.mod b/go.mod index 74eb2575..f214b9d3 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( require ( github.com/AlecAivazis/survey/v2 v2.3.7 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.17.0 // indirect github.com/google/go-github/v30 v30.1.0 // indirect @@ -52,3 +52,5 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/nats-io/jwt/v2 => /Users/aricart/src/github.com/nats-io/jwt/v2 diff --git a/go.sum b/go.sum index 804d24d2..413ae56a 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,9 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650= github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -69,8 +70,6 @@ github.com/nats-io/cliprompts/v2 v2.0.0-20231014115920-801ca035562a h1:28qvB6peS github.com/nats-io/cliprompts/v2 v2.0.0-20231014115920-801ca035562a/go.mod h1:oweZn7AeaVJYKlNHfCIhznJVsdySLSng55vfuINE/d0= github.com/nats-io/jsm.go v0.1.2 h1:T4Fq88a03sPAPWYwrOLQ85oanYsC2Bs6517rUiWBMpQ= github.com/nats-io/jsm.go v0.1.2/go.mod h1:tnubE70CAKi5TNfQiq6XHFqWTuSIe1H7X4sDwfq6ZK8= -github.com/nats-io/jwt/v2 v2.6.0 h1:yXoBTdEotZw3NujMT+Nnu1UPNlFWdKQ3d0JJF/+pJag= -github.com/nats-io/jwt/v2 v2.6.0/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.10.18 h1:tRdZmBuWKVAFYtayqlBB2BuCHNGAQPvoQIXOKwU3WSM= github.com/nats-io/nats-server/v2 v2.10.18/go.mod h1:97Qyg7YydD8blKlR8yBsUlPlWyZKjA7Bp5cl3MUE9K8= github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=