Skip to content

Commit 2931e19

Browse files
authored
Tags are now case-sensitive, describe account lists tags associated with the account (#668)
- [CHANGE] Tags (--tag and --rm-tag) are now case-sensitive - [FIX] describe accounts was not printing tags associated with the account - [FEAT] added option `--strict-tags` to edit operator/account/user, this flag enables use of mix-case values on tags. If not specified and a tag value is mixed-cased, the edit will be rejected. - [FEAT] for `--rm-tag` options in operator/account/user, the operation will now fail if the value is not found the JWT.
1 parent 31bb80f commit 2931e19

File tree

11 files changed

+132
-32
lines changed

11 files changed

+132
-32
lines changed

cmd/describer.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,7 @@ func AddStandardClaimInfo(table *tablewriter.Table, claims jwt.Claims) {
443443
}
444444
tags = ac.Tags
445445
}
446-
if acc, ok := claims.(*jwt.ActivationClaims); ok {
447-
if acc.IssuerAccount != "" {
448-
issuer = acc.IssuerAccount
449-
}
446+
if acc, ok := claims.(*jwt.AccountClaims); ok {
450447
tags = acc.Tags
451448
}
452449
if uc, ok := claims.(*jwt.UserClaims); ok {

cmd/editaccount.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func createEditAccount() *cobra.Command {
5757
return RunAction(cmd, args, params)
5858
},
5959
}
60+
cmd.Flags().BoolVarP(&params.strictTags, "strict-tags", "", false, "allow tags to be case-sensitive, default false")
6061
cmd.Flags().StringSliceVarP(&params.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times")
6162
cmd.Flags().StringSliceVarP(&params.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times")
6263
params.conns = -1

cmd/editaccount_test.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func Test_EditAccount(t *testing.T) {
3737
{createEditAccount(), []string{"edit", "account"}, nil, []string{"specify an edit option"}, true},
3838
{createEditAccount(), []string{"edit", "account", "--info-url", "http://foo/bar"}, nil, []string{"changed info url to"}, false},
3939
{createEditAccount(), []string{"edit", "account", "--description", "my account is about this"}, nil, []string{"changed description to"}, false},
40-
{createEditAccount(), []string{"edit", "account", "--tag", "A", "--name", "A"}, nil, []string{"edited account \"A\""}, false},
40+
{createEditAccount(), []string{"edit", "account", "--tag", "a", "--name", "A"}, nil, []string{"edited account \"A\""}, false},
4141
}
4242

4343
tests.Run(t, "root", "edit")
@@ -60,7 +60,7 @@ func Test_EditAccount_Tag(t *testing.T) {
6060
defer ts.Done(t)
6161

6262
ts.AddAccount(t, "A")
63-
_, _, err := ExecuteCmd(createEditAccount(), "--tag", "A,B,C")
63+
_, _, err := ExecuteCmd(createEditAccount(), "--tag", "a,b,c")
6464
require.NoError(t, err)
6565

6666
ac, err := ts.Store.ReadAccountClaim("A")
@@ -75,17 +75,17 @@ func Test_EditAccount_RmTag(t *testing.T) {
7575
defer ts.Done(t)
7676

7777
ts.AddAccount(t, "A")
78-
_, _, err := ExecuteCmd(createEditAccount(), "--tag", "A,B,C")
78+
_, _, err := ExecuteCmd(createEditAccount(), "--tag", "A,B,C", "--strict-tags")
7979
require.NoError(t, err)
8080

81-
_, _, err = ExecuteCmd(createEditAccount(), "--rm-tag", "A,B")
81+
_, _, err = ExecuteCmd(createEditAccount(), "--rm-tag", "A,B", "--strict-tags")
8282
require.NoError(t, err)
8383

8484
ac, err := ts.Store.ReadAccountClaim("A")
8585
require.NoError(t, err)
8686

8787
require.Len(t, ac.Tags, 1)
88-
require.ElementsMatch(t, ac.Tags, []string{"c"})
88+
require.ElementsMatch(t, ac.Tags, []string{"C"})
8989
}
9090

9191
func Test_EditAccount_Times(t *testing.T) {
@@ -380,17 +380,17 @@ func Test_EditSysAccount(t *testing.T) {
380380
for idx, n := range jsOptions {
381381
flag := fmt.Sprintf("--%s", n)
382382
if idx > 0 {
383-
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "A", flag, "1")
383+
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "a", flag, "1")
384384
require.Error(t, err)
385385
require.Contains(t, err.Error(), flag)
386386
} else {
387-
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "A", flag)
387+
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "a", flag)
388388
require.Error(t, err)
389389
require.Contains(t, err.Error(), flag)
390390
}
391391
}
392392
// defaults are removed automatically
393-
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "A")
393+
_, _, err = ExecuteCmd(createEditAccount(), "SYS", "--tag", "a")
394394
require.NoError(t, err)
395395
}
396396

@@ -524,3 +524,28 @@ func Test_EnableTierNoOtherFlag(t *testing.T) {
524524
require.Error(t, err)
525525
require.Equal(t, "rm-js-tier is exclusive of all other js options", err.Error())
526526
}
527+
528+
func TestEditAccountStrictTags(t *testing.T) {
529+
ts := NewTestStore(t, "O")
530+
defer ts.Done(t)
531+
532+
ts.AddAccount(t, "A")
533+
534+
_, _, err := ExecuteCmd(createEditAccount(), "--tag", "a")
535+
require.NoError(t, err)
536+
537+
_, _, err = ExecuteCmd(createEditAccount(), "--rm-tag", "A")
538+
require.Error(t, err)
539+
require.Contains(t, err.Error(), "--rm-tag \"A\" is not lowercased")
540+
541+
_, _, err = ExecuteCmd(createEditAccount(), "--rm-tag", "A", "--strict-tags")
542+
require.Error(t, err)
543+
require.Contains(t, err.Error(), "unable to remove tag: \"A\" - not found")
544+
545+
_, _, err = ExecuteCmd(createEditAccount(), "--tag", "A", "--strict-tags")
546+
require.NoError(t, err)
547+
548+
uc, err := ts.Store.ReadAccountClaim("A")
549+
require.NoError(t, err)
550+
require.True(t, uc.Tags.Equals(&jwt.TagList{"A", "a"}))
551+
}

cmd/editoperator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func createEditOperatorCmd() *cobra.Command {
4040
}
4141
params.signingKeys.BindFlags("sk", "", nkeys.PrefixByteOperator, cmd)
4242
cmd.Flags().StringSliceVarP(&params.rmSigningKeys, "rm-sk", "", nil, "remove signing key - comma separated list or option can be specified multiple times")
43+
cmd.Flags().BoolVarP(&params.strictTags, "strict-tags", "", false, "allow tags to be case-sensitive, default false")
4344
cmd.Flags().StringSliceVarP(&params.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times")
4445
cmd.Flags().StringSliceVarP(&params.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times")
4546
cmd.Flags().StringVarP(&params.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)")

cmd/editoperator_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func Test_EditOperator(t *testing.T) {
4343
{createEditOperatorCmd(), []string{"edit", "operator", "--sk"}, nil, []string{"flag needs an argument"}, true},
4444
{createEditOperatorCmd(), []string{"edit", "operator", "--sk", "SAADOZRUTPZS6LIXS6CSSSW5GXY3DNMQMSDTVWHQNHQTIBPGNSADSMBPEU"}, nil, []string{"invalid operator signing key"}, true},
4545
{createEditOperatorCmd(), []string{"edit", "operator", "--sk", "OBMWGGURAFWMH3AFDX65TVIH4ZYSL7UKZ3LOH2ZRWIAU7PGZ3IJNR6W5"}, nil, []string{"edited operator"}, false},
46-
{createEditOperatorCmd(), []string{"edit", "operator", "--tag", "O", "--start", "2019-04-13", "--expiry", "2050-01-01"}, nil, []string{"edited operator"}, false},
46+
{createEditOperatorCmd(), []string{"edit", "operator", "--tag", "o", "--start", "2019-04-13", "--expiry", "2050-01-01"}, nil, []string{"edited operator"}, false},
4747
{createEditOperatorCmd(), []string{"edit", "operator", "--require-signing-keys"}, nil, []string{"needs to be issued with a signing key first"}, true},
4848
}
4949

@@ -420,3 +420,29 @@ func Test_CannotSetRequireSKWithoutSK(t *testing.T) {
420420
require.False(t, oc.StrictSigningKeyUsage)
421421
require.Empty(t, oc.SigningKeys)
422422
}
423+
424+
func TestEditOperatorStrictTags(t *testing.T) {
425+
ts := NewTestStore(t, "O")
426+
defer ts.Done(t)
427+
428+
_, _, err := ExecuteCmd(createEditOperatorCmd(), "--tag", "A")
429+
require.Error(t, err)
430+
431+
_, _, err = ExecuteCmd(createEditOperatorCmd(), "--tag", "a")
432+
require.NoError(t, err)
433+
434+
_, _, err = ExecuteCmd(createEditOperatorCmd(), "--rm-tag", "A")
435+
require.Error(t, err)
436+
require.Contains(t, err.Error(), "--rm-tag \"A\" is not lowercased")
437+
438+
_, _, err = ExecuteCmd(createEditOperatorCmd(), "--rm-tag", "A", "--strict-tags")
439+
require.Error(t, err)
440+
require.Contains(t, err.Error(), "unable to remove tag: \"A\" - not found")
441+
442+
_, _, err = ExecuteCmd(createEditOperatorCmd(), "--tag", "A", "--strict-tags")
443+
require.NoError(t, err)
444+
445+
oc, err := ts.Store.ReadOperatorClaim()
446+
require.NoError(t, err)
447+
require.True(t, oc.Tags.Equals(&jwt.TagList{"A", "a"}))
448+
}

cmd/edituser.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ nsc edit user --name <n> --rm-response-perms
7171
return RunAction(cmd, args, &params)
7272
},
7373
}
74+
cmd.Flags().BoolVarP(&params.strictTags, "strict-tags", "", false, "allow tags to be case-sensitive, default false")
7475
cmd.Flags().StringSliceVarP(&params.tags, "tag", "", nil, "add tags for user - comma separated list or option can be specified multiple times")
7576
cmd.Flags().StringSliceVarP(&params.rmTags, "rm-tag", "", nil, "remove tag - comma separated list or option can be specified multiple times")
7677
cmd.Flags().StringVarP(&params.name, "name", "n", "", "user name")

cmd/edituser_test.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
package cmd
1717

1818
import (
19-
"github.com/nats-io/nsc/v2/cmd/store"
2019
"strings"
2120
"testing"
2221
"time"
2322

23+
"github.com/nats-io/nsc/v2/cmd/store"
24+
2425
"github.com/nats-io/nkeys"
2526

2627
cli "github.com/nats-io/cliprompts/v2"
@@ -38,12 +39,13 @@ func Test_EditUser(t *testing.T) {
3839

3940
tests := CmdTests{
4041
{createEditUserCmd(), []string{"edit", "user"}, nil, []string{"specify an edit option"}, true},
41-
{createEditUserCmd(), []string{"edit", "user", "--tag", "A", "--account", "A"}, nil, []string{"edited user \"a\""}, false},
42+
{createEditUserCmd(), []string{"edit", "user", "--tag", "A", "--account", "A"}, nil, []string{"--tag \"A\" is not lowercased"}, true},
43+
{createEditUserCmd(), []string{"edit", "user", "--tag", "a", "--account", "A"}, nil, []string{"edited user \"a\""}, false},
4244
{createEditUserCmd(), []string{"edit", "user", "--conn-type", "MQTT", "--rm-conn-type", "LEAFNODE", "--account", "A"}, nil, []string{"added connection type MQTT", "added connection type MQTT"}, false},
4345
{createEditUserCmd(), []string{"edit", "user", "--conn-type", "LEAFNODE_WS", "--account", "A"}, nil, []string{"added connection type LEAFNODE_WS"}, false},
4446
{createEditUserCmd(), []string{"edit", "user", "--conn-type", "MQTT_WS", "--account", "A"}, nil, []string{"added connection type MQTT_WS"}, false},
45-
{createEditUserCmd(), []string{"edit", "user", "--tag", "B", "--account", "B"}, nil, []string{"user name is required"}, true},
46-
{createEditUserCmd(), []string{"edit", "user", "--tag", "B", "--account", "B", "--name", "bb"}, nil, []string{"edited user \"bb\""}, false},
47+
{createEditUserCmd(), []string{"edit", "user", "--tag", "b", "--account", "B"}, nil, []string{"user name is required"}, true},
48+
{createEditUserCmd(), []string{"edit", "user", "--tag", "b", "--account", "B", "--name", "bb"}, nil, []string{"edited user \"bb\""}, false},
4749
}
4850

4951
tests.Run(t, "root", "edit")
@@ -107,25 +109,25 @@ func Test_EditUser_Tag(t *testing.T) {
107109
defer ts.Done(t)
108110

109111
ts.AddUser(t, "A", "a")
110-
_, _, err := ExecuteCmd(createEditUserCmd(), "--tag", "A,B,C")
112+
_, _, err := ExecuteCmd(createEditUserCmd(), "--tag", "A,B,C", "--strict-tags")
111113
require.NoError(t, err)
112114

113115
cc, err := ts.Store.ReadUserClaim("A", "a")
114116
require.NoError(t, err)
115117
require.NotNil(t, cc)
116118

117119
require.Len(t, cc.Tags, 3)
118-
require.ElementsMatch(t, cc.Tags, []string{"a", "b", "c"})
120+
require.ElementsMatch(t, cc.Tags, []string{"A", "B", "C"})
119121

120-
_, _, err = ExecuteCmd(createEditUserCmd(), "--rm-tag", "A,B")
122+
_, _, err = ExecuteCmd(createEditUserCmd(), "--rm-tag", "A,B", "--strict-tags")
121123
require.NoError(t, err)
122124

123125
cc, err = ts.Store.ReadUserClaim("A", "a")
124126
require.NoError(t, err)
125127
require.NotNil(t, cc)
126128

127129
require.Len(t, cc.Tags, 1)
128-
require.ElementsMatch(t, cc.Tags, []string{"c"})
130+
require.ElementsMatch(t, cc.Tags, []string{"C"})
129131

130132
}
131133

@@ -595,3 +597,29 @@ func Test_EditUserConnectionDeleteCase(t *testing.T) {
595597
require.NoError(t, err)
596598
require.Len(t, claim.AllowedConnectionTypes, 0)
597599
}
600+
601+
func TestEditUserStrictTags(t *testing.T) {
602+
ts := NewTestStore(t, "O")
603+
defer ts.Done(t)
604+
605+
ts.AddAccount(t, "A")
606+
ts.AddUser(t, "A", "U")
607+
608+
_, _, err := ExecuteCmd(createEditUserCmd(), "--tag", "a")
609+
require.NoError(t, err)
610+
611+
_, _, err = ExecuteCmd(createEditUserCmd(), "--rm-tag", "A")
612+
require.Error(t, err)
613+
require.Contains(t, err.Error(), "--rm-tag \"A\" is not lowercased")
614+
615+
_, _, err = ExecuteCmd(createEditUserCmd(), "--rm-tag", "A", "--strict-tags")
616+
require.Error(t, err)
617+
require.Contains(t, err.Error(), "unable to remove tag: \"A\" - not found")
618+
619+
_, _, err = ExecuteCmd(createEditUserCmd(), "--tag", "A", "--strict-tags")
620+
require.NoError(t, err)
621+
622+
uc, err := ts.Store.ReadUserClaim("A", "U")
623+
require.NoError(t, err)
624+
require.True(t, uc.Tags.Equals(&jwt.TagList{"A", "a"}))
625+
}

cmd/genericclaimparams.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import (
3030
// GenericClaimsParams - TimeParams and tags
3131
type GenericClaimsParams struct {
3232
TimeParams
33-
tags []string
34-
rmTags []string
33+
tags []string
34+
rmTags []string
35+
strictTags bool
3536
}
3637

3738
func (sp *GenericClaimsParams) Edit(current []string) error {
@@ -100,6 +101,21 @@ func (sp *GenericClaimsParams) Valid() error {
100101
if err := sp.TimeParams.Validate(); err != nil {
101102
return err
102103
}
104+
if !sp.strictTags {
105+
for _, t := range sp.tags {
106+
tt := strings.ToLower(t)
107+
if t != tt {
108+
return fmt.Errorf("--tag %q is not lowercased, specify option --strict-tags to honor non-lowercased values", t)
109+
}
110+
}
111+
for _, t := range sp.rmTags {
112+
tt := strings.ToLower(t)
113+
if t != tt {
114+
return fmt.Errorf("--rm-tag %q is not lowercased, specify option --strict-tags", t)
115+
}
116+
}
117+
}
118+
103119
return nil
104120
}
105121

@@ -145,15 +161,19 @@ func (sp *GenericClaimsParams) Run(ctx ActionCtx, claim jwt.Claims, r *store.Rep
145161
}
146162

147163
tags.Add(sp.tags...)
148-
tags.Remove(sp.rmTags...)
164+
if err := tags.Remove(sp.rmTags...); err != nil {
165+
return err
166+
}
149167
sort.Strings(*tags)
150168

151169
if r != nil {
152170
for _, t := range sp.tags {
153-
r.AddOK("added tag %q", strings.ToLower(t))
171+
t = strings.TrimSpace(t)
172+
r.AddOK("added tag %q", t)
154173
}
155174
for _, t := range sp.rmTags {
156-
r.AddOK("removed tag %q", strings.ToLower(t))
175+
t = strings.TrimSpace(t)
176+
r.AddOK("removed tag %q", t)
157177
}
158178
}
159179
return nil

cmd/push_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func Test_SyncOK(t *testing.T) {
4040
ts.AddAccount(t, "A")
4141

4242
// edit the jwt
43-
_, _, err = ExecuteCmd(createEditAccount(), "--tag", "A")
43+
_, _, err = ExecuteCmd(createEditAccount(), "--tag", "A", "--strict-tags")
4444
require.NoError(t, err)
4545

4646
// sync the store
@@ -50,7 +50,8 @@ func Test_SyncOK(t *testing.T) {
5050
// verify the tag was stored
5151
ac, err := ts.Store.ReadAccountClaim("A")
5252
require.NoError(t, err)
53-
require.Contains(t, ac.Tags, "a")
53+
require.Contains(t, ac.Tags, "A")
54+
require.True(t, ac.Tags.Contains("A"))
5455
}
5556

5657
func Test_SyncNoURL(t *testing.T) {
@@ -120,7 +121,7 @@ func Test_SyncManualServer(t *testing.T) {
120121
ts.AddAccount(t, "A")
121122

122123
// edit the jwt
123-
_, _, err = ExecuteCmd(createEditAccount(), "--tag", "A")
124+
_, _, err = ExecuteCmd(createEditAccount(), "--tag", "a")
124125
require.NoError(t, err)
125126

126127
// sync the store

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/mitchellh/go-homedir v1.1.0
1010
github.com/nats-io/cliprompts/v2 v2.0.0-20231014115920-801ca035562a
1111
github.com/nats-io/jsm.go v0.1.2
12-
github.com/nats-io/jwt/v2 v2.6.0
12+
github.com/nats-io/jwt/v2 v2.6.1-0.20240917143920-d3fa85bd725f
1313
github.com/nats-io/nats-server/v2 v2.10.18
1414
github.com/nats-io/nats.go v1.37.0
1515
github.com/nats-io/nkeys v0.4.7

0 commit comments

Comments
 (0)