Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADDED] Import/Export.AllowTrace and Account.TraceDest #216

Merged
merged 2 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions v2/account_claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package jwt

import (
"errors"
"fmt"
"sort"
"time"

Expand Down Expand Up @@ -229,10 +230,16 @@ type Account struct {
DefaultPermissions Permissions `json:"default_permissions,omitempty"`
Mappings Mapping `json:"mappings,omitempty"`
Authorization ExternalAuthorization `json:"authorization,omitempty"`
Trace *MsgTrace `json:"trace,omitempty"`
Info
GenericFields
}

// MsgTrace holds distributed message tracing configuration
type MsgTrace struct {
Destination Subject `json:"dest,omitempty"`
}

// Validate checks if the account is valid, based on the wrapper
func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
a.Imports.Validate(acct.Subject, vr)
Expand All @@ -241,6 +248,16 @@ func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
a.DefaultPermissions.Validate(vr)
a.Mappings.Validate(vr)
a.Authorization.Validate(vr)
if a.Trace != nil {
tvr := CreateValidationResults()
a.Trace.Destination.Validate(tvr)
if !tvr.IsEmpty() {
vr.AddError(fmt.Sprintf("the account Trace.Destination %s", tvr.Issues[0].Description))
}
if a.Trace.Destination.HasWildCards() {
vr.AddError("the account Trace.Destination subject %q is not a valid publish subject", a.Trace.Destination)
}
}

if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports {
vr.AddError("the account contains more imports than allowed by the operator")
Expand Down
34 changes: 34 additions & 0 deletions v2/account_claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,37 @@ func TestAccountClaims_GetTags(t *testing.T) {
t.Fatal("expected tag bar")
}
}

func TestAccountClaimsTraceDest(t *testing.T) {
akp := createAccountNKey(t)
apk := publicKey(akp, t)

account := NewAccountClaims(apk)
for i, test := range []struct {
name string
invalidSubj Subject
expectErr bool
}{
{"trace not specified", "", false},
{"trace created but with empty destination", "", true},
{"trace dest has spaces", "invalid dest", true},
{"trace dest start with a dot", ".invalid.dest", true},
{"trace dest ends with a dot", "invalid.dest.", true},
{"trace dest has consecutive dots", "invalid..dest", true},
{"trace dest invalid publish dest", "invalid.publish.*.dest", true},
} {
t.Run(test.name, func(t *testing.T) {
if i > 0 {
account.Trace = &MsgTrace{Destination: test.invalidSubj}
}
vr := CreateValidationResults()
account.Validate(vr)

if test.expectErr && vr.IsEmpty() {
t.Fatal("account validation should have failed")
} else if !test.expectErr && !vr.IsEmpty() {
t.Fatalf("account validation should not have failed, got %+v", vr.Issues)
}
})
}
}
10 changes: 8 additions & 2 deletions v2/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type Export struct {
Latency *ServiceLatency `json:"service_latency,omitempty"`
AccountTokenPosition uint `json:"account_token_position,omitempty"`
Advertise bool `json:"advertise,omitempty"`
AllowTrace bool `json:"allow_trace,omitempty"`
Info
}

Expand Down Expand Up @@ -160,8 +161,13 @@ func (e *Export) Validate(vr *ValidationResults) {
if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() {
vr.AddError("invalid response type for service: %q", e.ResponseType)
}
if e.IsStream() && e.ResponseType != "" {
vr.AddError("invalid response type for stream: %q", e.ResponseType)
if e.IsStream() {
if e.ResponseType != "" {
vr.AddError("invalid response type for stream: %q", e.ResponseType)
}
if e.AllowTrace {
vr.AddError("AllowTrace only valid for service export")
}
}
if e.Latency != nil {
if !e.IsService() {
Expand Down
22 changes: 22 additions & 0 deletions v2/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package jwt

import (
"sort"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -440,3 +441,24 @@ func TestExport_ResponseThreshold(t *testing.T) {
t.Fatal("expected this to fail due to negative duration")
}
}

func TestExportAllowTrace(t *testing.T) {
// AllowTrace is only applicable to ServiceExport
e := &Export{Subject: "foo", Type: Stream, AllowTrace: true}
vr := CreateValidationResults()
e.Validate(vr)
if vr.IsEmpty() {
t.Fatalf("AllowTrace on stream should have an validation issue")
}
issue := vr.Issues[0]
if !strings.Contains(issue.Description, "AllowTrace only valid for service export") {
t.Fatalf("AllowTrace should be valid only for service export, got %q", issue.Description)
}

e.Type = Service
vr = CreateValidationResults()
e.Validate(vr)
if !vr.IsEmpty() {
t.Fatalf("validation should have been ok, got %+v", vr.Issues)
}
}
4 changes: 4 additions & 0 deletions v2/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Import struct {
LocalSubject RenamingSubject `json:"local_subject,omitempty"`
Type ExportType `json:"type,omitempty"`
Share bool `json:"share,omitempty"`
AllowTrace bool `json:"allow_trace,omitempty"`
}

// IsService returns true if the import is of type service
Expand All @@ -66,6 +67,9 @@ func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
if !i.IsService() && !i.IsStream() {
vr.AddError("invalid import type: %q", i.Type)
}
if i.IsService() && i.AllowTrace {
vr.AddError("AllowTrace only valid for stream import")
}

if i.Account == "" {
vr.AddError("account to import from is not specified")
Expand Down
24 changes: 24 additions & 0 deletions v2/imports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,27 @@ func TestImports_Validate(t *testing.T) {
}
}
}

func TestImportAllowTrace(t *testing.T) {
ak2 := createAccountNKey(t)
akp2 := publicKey(ak2, t)

// AllowTrace is only applicable to StreamImport
i := &Import{Subject: "foo", Account: akp2, Type: Service, AllowTrace: true}
vr := CreateValidationResults()
i.Validate("", vr)
if vr.IsEmpty() {
t.Fatalf("AllowTrace on service should have an validation issue")
}
issue := vr.Issues[0]
if !strings.Contains(issue.Description, "AllowTrace only valid for stream import") {
t.Fatalf("AllowTrace should be valid only for stream import, got %q", issue.Description)
}

i.Type = Stream
vr = CreateValidationResults()
i.Validate("", vr)
if !vr.IsEmpty() {
t.Fatalf("validation should have been ok, got %+v", vr.Issues)
}
}
8 changes: 8 additions & 0 deletions v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,18 @@ func (s Subject) Validate(vr *ValidationResults) {
v := string(s)
if v == "" {
vr.AddError("subject cannot be empty")
// No other checks after that make sense
return
}
if strings.Contains(v, " ") {
vr.AddError("subject %q cannot have spaces", v)
}
if v[0] == '.' || v[len(v)-1] == '.' {
vr.AddError("subject %q cannot start or end with a `.`", v)
}
if strings.Contains(v, "..") {
vr.AddError("subject %q cannot contain consecutive `.`", v)
}
}

func (s Subject) countTokenWildcards() int {
Expand Down
21 changes: 21 additions & 0 deletions v2/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,27 @@ func TestSubjectValid(t *testing.T) {
t.Fatalf("Subjects cannot have spaces")
}

s = ".start.with.dot"
vr = CreateValidationResults()
s.Validate(vr)
if vr.IsEmpty() || !strings.Contains(vr.Issues[0].Description, "start or end with a `.`") {
t.Fatalf("Did not get expected failure: %+v", vr.Issues)
}

s = "end.with.dot."
vr = CreateValidationResults()
s.Validate(vr)
if vr.IsEmpty() || !strings.Contains(vr.Issues[0].Description, "start or end with a `.`") {
t.Fatalf("Did not get expected failure: %+v", vr.Issues)
}

s = "consecutive..dot"
vr = CreateValidationResults()
s.Validate(vr)
if vr.IsEmpty() || !strings.Contains(vr.Issues[0].Description, "consecutive `.`") {
t.Fatalf("Did not get expected failure: %+v", vr.Issues)
}

s = "one"
vr = CreateValidationResults()
s.Validate(vr)
Expand Down
Loading