[fixed] issue where js overwrote leafnode remotes permissions from creds

Fixes #2415. We did a set instead of merge.
changes in `jwt_test.go` are to make the `createUserWithLimit` usable by my new test.

Signed-off-by: Matthias Hanel <mh@synadia.com>
This commit is contained in:
Matthias Hanel
2021-08-10 18:15:07 -04:00
parent f4297bc8f9
commit db9fd45be2
3 changed files with 145 additions and 23 deletions

View File

@@ -3864,7 +3864,7 @@ func newTimeRange(start time.Time, dur time.Duration) jwt.TimeRange {
return jwt.TimeRange{Start: start.Format("15:04:05"), End: start.Add(dur).Format("15:04:05")}
}
func createUserWithLimit(t *testing.T, accKp nkeys.KeyPair, expiration time.Time, limits func(*jwt.Limits)) string {
func createUserWithLimit(t *testing.T, accKp nkeys.KeyPair, expiration time.Time, limits func(*jwt.UserPermissionLimits)) string {
t.Helper()
ukp, _ := nkeys.CreateUser()
seed, _ := ukp.Seed()
@@ -3872,7 +3872,7 @@ func createUserWithLimit(t *testing.T, accKp nkeys.KeyPair, expiration time.Time
uclaim := newJWTTestUserClaims()
uclaim.Subject = upub
if limits != nil {
limits(&uclaim.Limits)
limits(&uclaim.UserPermissionLimits)
}
if !expiration.IsZero() {
uclaim.Expires = expiration.Unix()
@@ -3909,31 +3909,33 @@ func TestJWTUserLimits(t *testing.T) {
defer sA.Shutdown()
for _, v := range []struct {
pass bool
f func(*jwt.Limits)
f func(*jwt.UserPermissionLimits)
}{
{true, nil},
{false, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8") }},
{true, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/0") }},
{true, func(j *jwt.Limits) { j.Src.Set("127.0.0.1/8") }},
{true, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8,127.0.0.1/8") }},
{false, func(j *jwt.Limits) { j.Src.Set("8.8.8.8/8,9.9.9.9/8") }},
{true, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now(), time.Hour)) }},
{false, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now().Add(time.Hour), time.Hour)) }},
{true, func(j *jwt.Limits) {
{false, func(j *jwt.UserPermissionLimits) { j.Src.Set("8.8.8.8/8") }},
{true, func(j *jwt.UserPermissionLimits) { j.Src.Set("8.8.8.8/0") }},
{true, func(j *jwt.UserPermissionLimits) { j.Src.Set("127.0.0.1/8") }},
{true, func(j *jwt.UserPermissionLimits) { j.Src.Set("8.8.8.8/8,127.0.0.1/8") }},
{false, func(j *jwt.UserPermissionLimits) { j.Src.Set("8.8.8.8/8,9.9.9.9/8") }},
{true, func(j *jwt.UserPermissionLimits) { j.Times = append(j.Times, newTimeRange(time.Now(), time.Hour)) }},
{false, func(j *jwt.UserPermissionLimits) {
j.Times = append(j.Times, newTimeRange(time.Now().Add(time.Hour), time.Hour))
}},
{true, func(j *jwt.UserPermissionLimits) {
j.Times = append(j.Times, newTimeRange(inAnHour, time.Hour), newTimeRange(time.Now(), time.Hour))
}}, // last one is within range
{false, func(j *jwt.Limits) {
{false, func(j *jwt.UserPermissionLimits) {
j.Times = append(j.Times, newTimeRange(inAnHour, time.Hour), newTimeRange(inTwoHours, time.Hour))
}}, // out of range
{false, func(j *jwt.Limits) {
{false, func(j *jwt.UserPermissionLimits) {
j.Times = append(j.Times, newTimeRange(inAnHour, 3*time.Hour), newTimeRange(inTwoHours, 2*time.Hour))
}}, // overlapping [a[]b] out of range*/
{false, func(j *jwt.Limits) {
{false, func(j *jwt.UserPermissionLimits) {
j.Times = append(j.Times, newTimeRange(inAnHour, 3*time.Hour), newTimeRange(inTwoHours, time.Hour))
}}, // overlapping [a[b]] out of range
// next day tests where end < begin
{true, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now(), 25*time.Hour)) }},
{true, func(j *jwt.Limits) { j.Times = append(j.Times, newTimeRange(time.Now(), -time.Hour)) }},
{true, func(j *jwt.UserPermissionLimits) { j.Times = append(j.Times, newTimeRange(time.Now(), 25*time.Hour)) }},
{true, func(j *jwt.UserPermissionLimits) { j.Times = append(j.Times, newTimeRange(time.Now(), -time.Hour)) }},
} {
t.Run("", func(t *testing.T) {
creds := createUserWithLimit(t, kp, doNotExpire, v.f)
@@ -3976,7 +3978,7 @@ func TestJWTTimeExpiration(t *testing.T) {
for _, l := range []string{"", "Europe/Berlin", "America/New_York"} {
t.Run("simple expiration "+l, func(t *testing.T) {
start := time.Now()
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.Limits) {
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.UserPermissionLimits) {
if l == "" {
j.Times = []jwt.TimeRange{newTimeRange(start, validFor)}
} else {
@@ -4020,7 +4022,7 @@ func TestJWTTimeExpiration(t *testing.T) {
t.Run("double expiration", func(t *testing.T) {
start1 := time.Now()
start2 := start1.Add(2 * validFor)
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.Limits) {
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.UserPermissionLimits) {
j.Times = []jwt.TimeRange{newTimeRange(start1, validFor), newTimeRange(start2, validFor)}
})
defer removeFile(t, creds)
@@ -4059,7 +4061,7 @@ func TestJWTTimeExpiration(t *testing.T) {
})
t.Run("lower jwt expiration overwrites time", func(t *testing.T) {
start := time.Now()
creds := createUserWithLimit(t, kp, start.Add(validFor), func(j *jwt.Limits) { j.Times = []jwt.TimeRange{newTimeRange(start, 2*validFor)} })
creds := createUserWithLimit(t, kp, start.Add(validFor), func(j *jwt.UserPermissionLimits) { j.Times = []jwt.TimeRange{newTimeRange(start, 2*validFor)} })
defer removeFile(t, creds)
disconnectChan := make(chan struct{})
defer close(disconnectChan)
@@ -4114,7 +4116,7 @@ func TestJWTLimits(t *testing.T) {
errChan := make(chan struct{})
defer close(errChan)
t.Run("subs", func(t *testing.T) {
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.Limits) { j.Subs = 1 })
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.UserPermissionLimits) { j.Subs = 1 })
defer removeFile(t, creds)
c := natsConnect(t, sA.ClientURL(), nats.UserCredentials(creds),
nats.DisconnectErrHandler(func(conn *nats.Conn, err error) {
@@ -4133,7 +4135,7 @@ func TestJWTLimits(t *testing.T) {
chanRecv(t, errChan, time.Second)
})
t.Run("payload", func(t *testing.T) {
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.Limits) { j.Payload = 5 })
creds := createUserWithLimit(t, kp, doNotExpire, func(j *jwt.UserPermissionLimits) { j.Payload = 5 })
defer removeFile(t, creds)
c := natsConnect(t, sA.ClientURL(), nats.UserCredentials(creds))
defer c.Close()

View File

@@ -1362,8 +1362,7 @@ func (c *client) processLeafNodeConnect(s *Server, arg []byte, lang string) erro
// If we have JS enabled and the other side does as well we need to add in an import deny clause.
if jsConfigured && proto.JetStream {
// We should never have existing perms here, if that changes this needs to be reworked.
c.setPermissions(&Permissions{Publish: &SubjectPermission{Deny: []string{jsAllAPI}}})
c.mergePubDenyPermissions([]string{jsAllAPI})
}
// Set the Ping timer

View File

@@ -3786,3 +3786,124 @@ func TestLeafNodeUniqueServerNameCrossJSDomain(t *testing.T) {
test(sA, sA.ID(), sA, sL)
})
}
func TestLeafNodeJwtPermsAndJetStreamDomains(t *testing.T) {
createAcc := func(js bool) (string, string, nkeys.KeyPair) {
kp, _ := nkeys.CreateAccount()
aPub, _ := kp.PublicKey()
claim := jwt.NewAccountClaims(aPub)
if js {
claim.Limits.JetStreamLimits = jwt.JetStreamLimits{
MemoryStorage: 1024 * 1024,
DiskStorage: 1024 * 1024,
Streams: 1, Consumer: 2}
}
aJwt, err := claim.Encode(oKp)
require_NoError(t, err)
return aPub, aJwt, kp
}
sysPub, sysJwt, sysKp := createAcc(false)
accPub, accJwt, accKp := createAcc(true)
noExpiration := time.Now().Add(time.Hour)
// create user for acc to be used in leaf node.
lnCreds := createUserWithLimit(t, accKp, noExpiration, func(j *jwt.UserPermissionLimits) {
j.Sub.Deny.Add("subdeny")
j.Pub.Deny.Add("pubdeny")
})
defer removeFile(t, lnCreds)
unlimitedCreds := createUserWithLimit(t, accKp, noExpiration, nil)
defer removeFile(t, unlimitedCreds)
sysCreds := createUserWithLimit(t, sysKp, noExpiration, nil)
defer removeFile(t, sysCreds)
tmplA := `
operator: %s
system_account: %s
resolver: MEMORY
resolver_preload: {
%s: %s
%s: %s
}
listen: localhost:-1
leafnodes: {
listen: localhost:-1
}
jetstream :{
domain: "cluster"
store_dir: "%s"
max_mem: 100Mb
max_file: 100Mb
}
`
tmplL := `
listen: localhost:-1
accounts :{
A:{ jetstream: enable, users:[ {user:a1,password:a1}]},
SYS:{ users:[ {user:s1,password:s1}]},
}
system_account = SYS
jetstream: {
domain: ln1
store_dir: %s
max_mem: 50Mb
max_file: 50Mb
}
leafnodes:{
remotes:[{ url:nats://localhost:%d, account: A, credentials: %s},
{ url:nats://localhost:%d, account: SYS, credentials: %s}]
}
`
confA := createConfFile(t, []byte(fmt.Sprintf(tmplA, ojwt, sysPub,
sysPub, sysJwt, accPub, accJwt,
createDir(t, JetStreamStoreDir))))
defer removeFile(t, confA)
sA, _ := RunServerWithConfig(confA)
defer sA.Shutdown()
confL := createConfFile(t, []byte(fmt.Sprintf(tmplL, createDir(t, JetStreamStoreDir),
sA.opts.LeafNode.Port, lnCreds, sA.opts.LeafNode.Port, sysCreds)))
defer removeFile(t, confL)
sL, _ := RunServerWithConfig(confL)
defer sL.Shutdown()
checkLeafNodeConnectedCount(t, sA, 2)
checkLeafNodeConnectedCount(t, sL, 2)
ncA := natsConnect(t, sA.ClientURL(), nats.UserCredentials(unlimitedCreds))
defer ncA.Close()
ncL := natsConnect(t, fmt.Sprintf("nats://a1:a1@localhost:%d", sL.opts.Port))
defer ncL.Close()
test := func(subject string, cSub, cPub *nats.Conn, pass bool) {
sub, err := cSub.SubscribeSync(subject)
require_NoError(t, err)
require_NoError(t, cSub.Flush())
require_NoError(t, cPub.Publish(subject, []byte("hello world")))
require_NoError(t, cPub.Flush())
m, err := sub.NextMsg(500 * time.Millisecond)
if pass {
require_NoError(t, err)
require_True(t, m.Subject == subject)
require_Equal(t, string(m.Data), "hello world")
} else {
require_True(t, err == nats.ErrTimeout)
}
}
t.Run("sub-on-ln-pass", func(t *testing.T) {
test("sub", ncL, ncA, true)
})
t.Run("sub-on-ln-fail", func(t *testing.T) {
test("subdeny", ncL, ncA, false)
})
t.Run("pub-on-ln-pass", func(t *testing.T) {
test("pub", ncA, ncL, true)
})
t.Run("pub-on-ln-fail", func(t *testing.T) {
test("pubdeny", ncA, ncL, false)
})
}