From e422e0cb401c48f67b983d8cc7efb723926d26c6 Mon Sep 17 00:00:00 2001 From: Matthias Hanel Date: Fri, 15 May 2020 14:40:07 -0400 Subject: [PATCH 1/2] Adding account_token_position to account exports Signed-off-by: Matthias Hanel --- exports.go | 38 ++++++++++++++++++++++----- exports_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/exports.go b/exports.go index 5578f98..767f355 100644 --- a/exports.go +++ b/exports.go @@ -17,6 +17,7 @@ package jwt import ( "fmt" + "strings" "time" ) @@ -71,13 +72,14 @@ func (sl *ServiceLatency) Validate(vr *ValidationResults) { // Export represents a single export type Export struct { - Name string `json:"name,omitempty"` - Subject Subject `json:"subject,omitempty"` - Type ExportType `json:"type,omitempty"` - TokenReq bool `json:"token_req,omitempty"` - Revocations RevocationList `json:"revocations,omitempty"` - ResponseType ResponseType `json:"response_type,omitempty"` - Latency *ServiceLatency `json:"service_latency,omitempty"` + Name string `json:"name,omitempty"` + Subject Subject `json:"subject,omitempty"` + Type ExportType `json:"type,omitempty"` + TokenReq bool `json:"token_req,omitempty"` + Revocations RevocationList `json:"revocations,omitempty"` + ResponseType ResponseType `json:"response_type,omitempty"` + Latency *ServiceLatency `json:"service_latency,omitempty"` + AccountTokenPosition uint `json:"account_token_position,omitempty"` } // IsService returns true if an export is for a service @@ -124,6 +126,28 @@ func (e *Export) Validate(vr *ValidationResults) { e.Latency.Validate(vr) } e.Subject.Validate(vr) + + if e.AccountTokenPosition > 0 { + if !e.Subject.HasWildCards() { + vr.AddError("Account Token Position can only be used when wildcard subjects: %s", e.Subject) + } else { + subj := string(e.Subject) + token := strings.Split(subj, ".") + tkCnt := uint(len(token)) + if e.AccountTokenPosition == tkCnt && strings.HasSuffix(subj, "*") { + // all well, check for last token being '>' happens below + } else if e.AccountTokenPosition < tkCnt { + if tk := token[e.AccountTokenPosition-1]; tk != "*" { + vr.AddError("Account Token Position %d matches '%s' but must match a * in: %s", + e.AccountTokenPosition, tk, e.Subject) + } + } else if !strings.HasSuffix(subj, ">") { + vr.AddError( + "Account Token Position %d exceeds length of subject '%s' and last token is not >", + e.AccountTokenPosition, e.Subject) + } + } + } } // Revoke enters a revocation by publickey using time.Now(). diff --git a/exports_test.go b/exports_test.go index b674c90..e7310c2 100644 --- a/exports_test.go +++ b/exports_test.go @@ -288,3 +288,71 @@ func TestExport_Sorting(t *testing.T) { t.Fatal("exports not sorted") } } + +func TestExportAccountTokenPos(t *testing.T) { + okp := createOperatorNKey(t) + akp := createAccountNKey(t) + apk := publicKey(akp, t) + tbl := map[Subject]uint{ + ">": 5, + "*": 1, + "foo.*": 2, + "foo.>": 2, + "foo.*.bar.*": 2, + "foo.*.bar.>": 2, + "*.*.*.>": 2, + "*.*.>": 1, + } + for k, v := range tbl { + t.Run(string(k), func(t *testing.T) { + account := NewAccountClaims(apk) + //account.Limits = OperatorLimits{} + account.Exports = append(account.Exports, + &Export{Type: Stream, Subject: k, AccountTokenPosition: v}) + actJwt := encode(account, okp, t) + account2, err := DecodeAccountClaims(actJwt) + if err != nil { + t.Fatal("error decoding account jwt", err) + } + AssertEquals(account.String(), account2.String(), t) + vr := &ValidationResults{} + account2.Validate(vr) + if len(vr.Issues) != 0 { + t.Fatal("validation issues", *vr) + } + }) + } +} + +func TestExportAccountTokenPosFail(t *testing.T) { + okp := createOperatorNKey(t) + akp := createAccountNKey(t) + apk := publicKey(akp, t) + tbl := map[Subject]uint{ + "foo.>": 1, + "*": 5, + "*.*": 5, + "bar": 1, + "foo.bar": 2, + "foo.*.bar": 3, + } + for k, v := range tbl { + t.Run(string(k), func(t *testing.T) { + account := NewAccountClaims(apk) + //account.Limits = OperatorLimits{} + account.Exports = append(account.Exports, + &Export{Type: Stream, Subject: k, AccountTokenPosition: v}) + actJwt := encode(account, okp, t) + account2, err := DecodeAccountClaims(actJwt) + if err != nil { + t.Fatal("error decoding account jwt", err) + } + AssertEquals(account.String(), account2.String(), t) + vr := &ValidationResults{} + account2.Validate(vr) + if len(vr.Issues) != 1 { + t.Fatal("validation issue expected", *vr) + } + }) + } +} From 7818608a3da0830e524e2df4f0c48afaab062cfc Mon Sep 17 00:00:00 2001 From: Matthias Hanel Date: Mon, 18 May 2020 21:21:02 -0400 Subject: [PATCH 2/2] Allow token position to only work with * --- exports.go | 17 ++++++----------- exports_test.go | 8 +++++--- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/exports.go b/exports.go index 767f355..9670442 100644 --- a/exports.go +++ b/exports.go @@ -129,22 +129,17 @@ func (e *Export) Validate(vr *ValidationResults) { if e.AccountTokenPosition > 0 { if !e.Subject.HasWildCards() { - vr.AddError("Account Token Position can only be used when wildcard subjects: %s", e.Subject) + vr.AddError("Account Token Position can only be used with wildcard subjects: %s", e.Subject) } else { subj := string(e.Subject) token := strings.Split(subj, ".") tkCnt := uint(len(token)) - if e.AccountTokenPosition == tkCnt && strings.HasSuffix(subj, "*") { - // all well, check for last token being '>' happens below - } else if e.AccountTokenPosition < tkCnt { - if tk := token[e.AccountTokenPosition-1]; tk != "*" { - vr.AddError("Account Token Position %d matches '%s' but must match a * in: %s", - e.AccountTokenPosition, tk, e.Subject) - } - } else if !strings.HasSuffix(subj, ">") { - vr.AddError( - "Account Token Position %d exceeds length of subject '%s' and last token is not >", + if e.AccountTokenPosition > tkCnt { + vr.AddError("Account Token Position %d exceeds length of subject '%s'", e.AccountTokenPosition, e.Subject) + } else if tk := token[e.AccountTokenPosition-1]; tk != "*" { + vr.AddError("Account Token Position %d matches '%s' but must match a * in: %s", + e.AccountTokenPosition, tk, e.Subject) } } } diff --git a/exports_test.go b/exports_test.go index e7310c2..5673802 100644 --- a/exports_test.go +++ b/exports_test.go @@ -294,10 +294,8 @@ func TestExportAccountTokenPos(t *testing.T) { akp := createAccountNKey(t) apk := publicKey(akp, t) tbl := map[Subject]uint{ - ">": 5, "*": 1, "foo.*": 2, - "foo.>": 2, "foo.*.bar.*": 2, "foo.*.bar.>": 2, "*.*.*.>": 2, @@ -329,12 +327,16 @@ func TestExportAccountTokenPosFail(t *testing.T) { akp := createAccountNKey(t) apk := publicKey(akp, t) tbl := map[Subject]uint{ - "foo.>": 1, + ">": 5, + "foo.>": 2, + "bar.>": 1, "*": 5, "*.*": 5, "bar": 1, "foo.bar": 2, "foo.*.bar": 3, + "*.>": 3, + "*.*.>": 3, } for k, v := range tbl { t.Run(string(k), func(t *testing.T) {