Repository: nats-io/nats-server
Pull request #1900 (2021-02-08 22:47:19)

Allow meta leader stepdown.
Also easier way to put authorization on system account and still use $G.

Signed-off-by: Derek Collison <derek@nats.io>

/cc @nats-io/core



Patch:
diff --git a/go.mod b/go.mod
index eb08e6b8..b5c73c70 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.15
 require (
 	github.com/klauspost/compress v1.11.7
 	github.com/minio/highwayhash v1.0.0
-	github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813
+	github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
 	github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a
 	github.com/nats-io/nkeys v0.2.0
 	github.com/nats-io/nuid v1.0.1
diff --git a/go.sum b/go.sum
index d2a5e843..39ea18ff 100644
--- a/go.sum
+++ b/go.sum
@@ -19,8 +19,8 @@ github.com/nats-io/jwt v1.1.0 h1:+vOlgtM0ZsF46GbmUoadq0/2rChNS45gtxHEa3H1gqM=
 github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M=
 github.com/nats-io/jwt/v2 v2.0.0-20200916203241-1f8ce17dff02/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
 github.com/nats-io/jwt/v2 v2.0.0-20201015190852-e11ce317263c/go.mod h1:vs+ZEjP+XKy8szkBmQwCB7RjYdIlMaPsFPs4VdS4bTQ=
-github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813 h1:km4lLzT86NyJRhO++VqfP/vn5cbfm+E05i2bGdqDbrY=
-github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
+github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc h1:pu+s4XC+bYnI0iD2vDtOl83zjCYUau/q6c83pEvsGZc=
+github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc/go.mod h1:PuO5FToRL31ecdFqVjc794vK0Bj0CwzveQEDvkb7MoQ=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200524125952-51ebd92a9093/go.mod h1:rQnBf2Rv4P9adtAs/Ti6LfFmVtFG6HLhl/H7cVshcJU=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200601203034-f8d6dd992b71/go.mod h1:Nan/1L5Sa1JRW+Thm4HNYcIDcVRFc5zK9OpSZeI2kk4=
 github.com/nats-io/nats-server/v2 v2.1.8-0.20200929001935-7f44d075f7ad/go.mod h1:TkHpUIDETmTI7mrHN40D1pzxfzHZuGmtMbtb83TGVQw=
diff --git a/server/accounts.go b/server/accounts.go
index 31f47bed..35bba947 100644
--- a/server/accounts.go
+++ b/server/accounts.go
@@ -506,9 +506,9 @@ func (a *Account) TotalSubs() int {
 
 // MapDest is for mapping published subjects for clients.
 type MapDest struct {
-	Subject string `json:"subject"`
-	Weight  uint8  `json:"weight"`
-	Cluster string `json:"cluster,omitempty"`
+	Subject    string `json:"subject"`
+	Weight     uint8  `json:"weight"`
+	OptCluster string `json:"cluster,omitempty"`
 }
 
 func NewMapDest(subject string, weight uint8) *MapDest {
@@ -573,16 +573,16 @@ func (a *Account) AddWeightedMappings(src string, dests ...*MapDest) error {
 		if err != nil {
 			return err
 		}
-		if d.Cluster == "" {
+		if d.OptCluster == "" {
 			m.dests = append(m.dests, &destination{tr, d.Weight})
 		} else {
 			// We have a cluster scoped filter.
 			if m.cdests == nil {
 				m.cdests = make(map[string][]*destination)
 			}
-			ad := m.cdests[d.Cluster]
+			ad := m.cdests[d.OptCluster]
 			ad = append(ad, &destination{tr, d.Weight})
-			m.cdests[d.Cluster] = ad
+			m.cdests[d.OptCluster] = ad
 		}
 	}
 
@@ -2027,9 +2027,7 @@ func (a *Account) createRespWildcard() []byte {
 	a.mu.Unlock()
 
 	// Create subscription and internal callback for all the wildcard response subjects.
-	if sub, err := c.processSub(wcsub, nil, []byte(sid), a.processServiceImportResponse, false); err == nil {
-		sub.rsi = true
-	}
+	c.processSub(wcsub, nil, []byte(sid), a.processServiceImportResponse, false)
 
 	return pre
 }
@@ -2873,32 +2871,8 @@ func (s *Server) updateAccountClaimsWithRefresh(a *Account, ac *jwt.AccountClaim
 			alteredScope[k] = struct{}{}
 		}
 	}
-	// collect mappings that need to be removed
-	removeList := []string{}
-	for _, m := range a.mappings {
-		if _, ok := ac.Mappings[jwt.Subject(m.src)]; !ok {
-			removeList = append(removeList, m.src)
-		}
-	}
 	a.mu.Unlock()
 
-	for sub, wm := range ac.Mappings {
-		mappings := make([]*MapDest, len(wm))
-		for i, m := range wm {
-			mappings[i] = &MapDest{
-				Subject: string(m.Subject),
-				Weight:  m.GetWeight(),
-				Cluster: m.Cluster,
-			}
-		}
-		// This will overwrite existing entries
-		a.AddWeightedMappings(string(sub), mappings...)
-	}
-	// remove mappings
-	for _, rmMapping := range removeList {
-		a.RemoveMapping(rmMapping)
-	}
-
 	gatherClients := func() []*client {
 		a.mu.RLock()
 		clients := make([]*client, 0, len(a.clients))
diff --git a/server/client.go b/server/client.go
index 6a7b4f7a..d328f69b 100644
--- a/server/client.go
+++ b/server/client.go
@@ -472,8 +472,7 @@ func (c *client) clientType() int {
 // to optionally have an opts section for non-normal stuff.
 type subscription struct {
 	client  *client
-	im      *streamImport // This is for import stream support.
-	rsi     bool
+	im      *streamImport   // This is for import stream support.
 	shadow  []*subscription // This is to track shadowed accounts.
 	icb     msgHandler
 	subject []byte
@@ -3886,7 +3885,7 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver,
 		}
 
 		// Remap to the original subject if internal.
-		if sub.icb != nil && sub.rsi {
+		if sub.icb != nil {
 			subj = subject
 		}
 
diff --git a/server/jetstream_api.go b/server/jetstream_api.go
index a0f6af6a..59df417d 100644
--- a/server/jetstream_api.go
+++ b/server/jetstream_api.go
@@ -157,6 +157,11 @@ const (
 	JSApiConsumerLeaderStepDown  = "$JS.API.CONSUMER.LEADER.STEPDOWN.*.*"
 	JSApiConsumerLeaderStepDownT = "$JS.API.CONSUMER.LEADER.STEPDOWN.%s.%s"
 
+	// JSApiLeaderStepDown is the endpoint to have our metaleader stepdown.
+	// Only works from system account.
+	// Will return JSON response.
+	JSApiLeaderStepDown = "$JS.API.META.LEADER.STEPDOWN"
+
 	// jsAckT is the template for the ack message stream coming back from a consumer
 	// when they ACK/NAK, etc a message.
 	jsAckT   = "$JS.ACK.%s.%s"
@@ -417,6 +422,14 @@ type JSApiConsumerLeaderStepDownResponse struct {
 
 const JSApiConsumerLeaderStepDownResponseType = "io.nats.jetstream.api.v1.consumer_leader_stepdown_response"
 
+// JSApiLeaderStepDownResponse is the response to a meta leader stepdown request.
+type JSApiLeaderStepDownResponse struct {
+	ApiResponse
+	Success bool `json:"success,omitempty"`
+}
+
+const JSApiLeaderStepDownResponseType = "io.nats.jetstream.api.v1.meta_leader_stepdown_response"
+
 // JSApiMsgGetRequest get a message request.
 type JSApiMsgGetRequest struct {
 	Seq uint64 `json:"seq"`
@@ -636,6 +649,9 @@ func (s *Server) getRequestInfo(c *client, raw []byte) (pci *ClientInfo, acc *Ac
 	} else {
 		// Direct $SYS access.
 		acc = c.acc
+		if acc == nil {
+			acc = s.SystemAccount()
+		}
 	}
 	if acc == nil {
 		return nil, nil, nil, nil, ErrMissingAccount
@@ -1657,6 +1673,45 @@ func (s *Server) jsStreamRemovePeerRequest(sub *subscription, c *client, subject
 	s.sendAPIResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(resp))
 }
 
+// Request to have the meta leader stepdown.
+// These will only be received the the meta leaders, so less checking needed.
+func (s *Server) jsLeaderStepDownRequest(sub *subscription, c *client, subject, reply string, rmsg []byte) {
+	if c == nil || !s.JetStreamEnabled() {
+		return
+	}
+
+	ci, acc, _, msg, err := s.getRequestInfo(c, rmsg)
+	if err != nil {
+		s.Warnf(badAPIRequestT, msg)
+		return
+	}
+
+	js, cc := s.getJetStreamCluster()
+	if js == nil || cc == nil || cc.meta == nil {
+		return
+	}
+
+	// Extra checks here but only leader is listening.
+	js.mu.RLock()
+	isLeader := cc.isLeader()
+	js.mu.RUnlock()
+
+	if !isLeader {
+		return
+	}
+
+	// Call actual stepdown.
+	err = cc.meta.StepDown()
+
+	var resp = JSApiLeaderStepDownResponse{ApiResponse: ApiResponse{Type: JSApiLeaderStepDownResponseType}}
+	if err != nil {
+		resp.Error = jsError(err)
+	} else {
+		resp.Success = true
+	}
+	s.sendAPIResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(resp))
+}
+
 func isEmptyRequest(req []byte) bool {
 	if len(req) == 0 {
 		return true
diff --git a/server/jetstream_cluster.go b/server/jetstream_cluster.go
index 821ad0ca..66475f9a 100644
--- a/server/jetstream_cluster.go
+++ b/server/jetstream_cluster.go
@@ -44,6 +44,8 @@ type jetStreamCluster struct {
 	// Processing assignment results.
 	streamResults   *subscription
 	consumerResults *subscription
+	// For asking for leader to stepdown.
+	stepdown *subscription
 }
 
 // Used to guide placement of streams in clustered JetStream.
@@ -2602,6 +2604,9 @@ func (js *jetStream) startUpdatesSub() {
 	if cc.consumerResults == nil {
 		cc.consumerResults, _ = s.systemSubscribe(consumerAssignmentSubj, _EMPTY_, false, c, js.processConsumerAssignmentResults)
 	}
+	if cc.stepdown == nil {
+		cc.stepdown, _ = s.systemSubscribe(JSApiLeaderStepDown, _EMPTY_, false, c, s.jsLeaderStepDownRequest)
+	}
 }
 
 // Lock should be held.
@@ -2615,6 +2620,10 @@ func (js *jetStream) stopUpdatesSub() {
 		cc.s.sysUnsubscribe(cc.consumerResults)
 		cc.consumerResults = nil
 	}
+	if cc.stepdown != nil {
+		cc.s.sysUnsubscribe(cc.stepdown)
+		cc.stepdown = nil
+	}
 }
 
 func (js *jetStream) processLeaderChange(isLeader bool) {
diff --git a/server/monitor.go b/server/monitor.go
index 540dea6e..e9618703 100644
--- a/server/monitor.go
+++ b/server/monitor.go
@@ -1047,7 +1047,6 @@ type Varz struct {
 	Tags                  jwt.TagList           `json:"tags,omitempty"`
 	TrustedOperatorsJwt   []string              `json:"trusted_operators_jwt,omitempty"`
 	TrustedOperatorsClaim []*jwt.OperatorClaims `json:"trusted_operators_claim,omitempty"`
-	SystemAccount         string                `json:"system_account,omitempty"`
 }
 
 // JetStreamVarz contains basic runtime information about jetstream
@@ -1335,9 +1334,6 @@ func (s *Server) updateVarzConfigReloadableFields(v *Varz) {
 		v.Cluster.URLs = urlsToStrings(opts.Routes)
 		s.varzUpdateRouteURLs = false
 	}
-	if s.sys != nil && s.sys.account != nil {
-		v.SystemAccount = s.sys.account.GetName()
-	}
 }
 
 // Updates the runtime Varz fields, that is, fields that change during
@@ -1993,7 +1989,6 @@ type ExtMap map[string][]*MapDest
 type AccountInfo struct {
 	AccountName string               `json:"account_name"`
 	LastUpdate  time.Time            `json:"update_time,omitempty"`
-	IsSystem    bool                 `json:"is_system,omitempty"`
 	Expired     bool                 `json:"expired"`
 	Complete    bool                 `json:"complete"`
 	JetStream   bool                 `json:"jetstream_enabled"`
@@ -2016,11 +2011,10 @@ type AccountInfo struct {
 }
 
 type Accountz struct {
-	ID            string       `json:"server_id"`
-	Now           time.Time    `json:"now"`
-	SystemAccount string       `json:"system_account,omitempty"`
-	Accounts      []string     `json:"accounts,omitempty"`
-	Account       *AccountInfo `json:"account_detail,omitempty"`
+	ID       string       `json:"server_id"`
+	Now      time.Time    `json:"now"`
+	Accounts []string     `json:"accounts,omitempty"`
+	Account  *AccountInfo `json:"account_detail,omitempty"`
 }
 
 // HandleAccountz process HTTP requests for account information.
@@ -2045,9 +2039,6 @@ func (s *Server) Accountz(optz *AccountzOptions) (*Accountz, error) {
 		ID:  s.ID(),
 		Now: time.Now(),
 	}
-	if sacc := s.SystemAccount(); sacc != nil {
-		a.SystemAccount = sacc.GetName()
-	}
 	if optz.Account == "" {
 		a.Accounts = []string{}
 		s.accounts.Range(func(key, value interface{}) bool {
@@ -2095,7 +2086,6 @@ func (s *Server) accountInfo(accName string) (*AccountInfo, error) {
 	} else {
 		a = v.(*Account)
 	}
-	isSys := a == s.SystemAccount()
 	a.mu.RLock()
 	defer a.mu.RUnlock()
 	var vrIssues []ExtVrIssues
@@ -2200,7 +2190,6 @@ func (s *Server) accountInfo(accName string) (*AccountInfo, error) {
 	return &AccountInfo{
 		accName,
 		a.updated,
-		isSys,
 		a.expired,
 		!a.incomplete,
 		a.js != nil,
diff --git a/server/opts.go b/server/opts.go
index 2915ea05..76c97304 100644
--- a/server/opts.go
+++ b/server/opts.go
@@ -1,4 +1,4 @@
-// Copyright 2012-2020 The NATS Authors
+// Copyright 2012-2021 The NATS Authors
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -2018,7 +2018,7 @@ func parseAccountMapDest(v interface{}, tk token, errors *[]error, warnings *[]e
 				return nil, err
 			}
 		case "cluster":
-			mdest.Cluster = dmv.(string)
+			mdest.OptCluster = dmv.(string)
 		default:
 			err := &configErr{tk, fmt.Sprintf("Unknown field %q for mapping destination", k)}
 			*errors = append(*errors, err)
diff --git a/server/reload.go b/server/reload.go
index fadfc4e9..ad783b56 100644
--- a/server/reload.go
+++ b/server/reload.go
@@ -1063,6 +1063,23 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) {
 				continue
 			}
 			fallthrough
+		case "noauthuser":
+			if oldValue != _EMPTY_ && newValue == _EMPTY_ {
+				for _, user := range newOpts.Users {
+					if user.Username == oldValue {
+						return nil, fmt.Errorf("config reload not supported for %s: old=%v, new=%v",
+							field.Name, oldValue, newValue)
+					}
+				}
+			} else {
+				return nil, fmt.Errorf("config reload not supported for %s: old=%v, new=%v",
+					field.Name, oldValue, newValue)
+			}
+		case "systemaccount":
+			if oldValue != DEFAULT_SYSTEM_ACCOUNT || newValue != _EMPTY_ {
+				return nil, fmt.Errorf("config reload not supported for %s: old=%v, new=%v",
+					field.Name, oldValue, newValue)
+			}
 		default:
 			// TODO(ik): Implement String() on those options to have a nice print.
 			// %v is difficult to figure what's what, %+v print private fields and
diff --git a/server/server.go b/server/server.go
index 9128e1da..aab4446b 100644
--- a/server/server.go
+++ b/server/server.go
@@ -625,6 +625,11 @@ func (s *Server) configureAccounts() error {
 		acc.sl = nil
 		acc.clients = nil
 		s.registerAccountNoLock(a)
+
+		// If we see an account defined using $SYS we will make sure that is set as system account.
+		if acc.Name == DEFAULT_SYSTEM_ACCOUNT && opts.SystemAccount == _EMPTY_ {
+			s.opts.SystemAccount = DEFAULT_SYSTEM_ACCOUNT
+		}
 	}
 
 	// Now that we have this we need to remap any referenced accounts in
@@ -638,7 +643,9 @@ func (s *Server) configureAccounts() error {
 			ea.approved[sub] = acc
 		}
 	}
+	var numAccounts int
 	s.accounts.Range(func(k, v interface{}) bool {
+		numAccounts++
 		acc := v.(*Account)
 		// Exports
 		for _, se := range acc.exports.streams {
@@ -697,6 +704,22 @@ func (s *Server) configureAccounts() error {
 		if err != nil {
 			return fmt.Errorf("error resolving system account: %v", err)
 		}
+
+		// If we have defined a system account here check to see if its just us and the $G account.
+		// We would do this to add user/pass to the system account. If this is the case add in
+		// no-auth-user for $G.
+		if numAccounts == 2 && s.opts.NoAuthUser == _EMPTY_ {
+			// Create a unique name so we do not collide.
+			var b [8]byte
+			rn := rand.Int63()
+			for i, l := 0, rn; i < len(b); i++ {
+				b[i] = digits[l%base]
+				l /= base
+			}
+			uname := fmt.Sprintf("nats-%s", b[:])
+			s.opts.Users = append(s.opts.Users, &User{Username: uname, Password: string(b[:]), Account: s.gacc})
+			s.opts.NoAuthUser = uname
+		}
 	}
 
 	return nil
diff --git a/vendor/github.com/nats-io/jwt/v2/account_claims.go b/vendor/github.com/nats-io/jwt/v2/account_claims.go
index 04065b6a..67169483 100644
--- a/vendor/github.com/nats-io/jwt/v2/account_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/account_claims.go
@@ -84,44 +84,6 @@ func (o *OperatorLimits) Validate(_ *ValidationResults) {
 	// negative values mean unlimited, so all numbers are valid
 }
 
-// Mapping for publishes
-type WeightedMapping struct {
-	Subject Subject `json:"subject"`
-	Weight  uint8   `json:"weight,omitempty"`
-	Cluster string  `json:"cluster,omitempty"`
-}
-
-func (m *WeightedMapping) GetWeight() uint8 {
-	if m.Weight == 0 {
-		return 100
-	}
-	return m.Weight
-}
-
-type Mapping map[Subject][]WeightedMapping
-
-func (m *Mapping) Validate(vr *ValidationResults) {
-	for ubFrom, wm := range (map[Subject][]WeightedMapping)(*m) {
-		ubFrom.Validate(vr)
-		total := uint8(0)
-		for _, wm := range wm {
-			wm.Subject.Validate(vr)
-			if wm.Subject.HasWildCards() {
-				vr.AddError("Subject %q in weighted mapping %q is not allowed to contains wildcard",
-					string(wm.Subject), ubFrom)
-			}
-			total += wm.GetWeight()
-		}
-		if total > 100 {
-			vr.AddError("Mapping %q exceeds 100%% among all of it's weighted to mappings", ubFrom)
-		}
-	}
-}
-
-func (a *Account) AddMapping(sub Subject, to ...WeightedMapping) {
-	a.Mappings[sub] = to
-}
-
 // Account holds account specific claims data
 type Account struct {
 	Imports            Imports        `json:"imports,omitempty"`
@@ -130,7 +92,6 @@ type Account struct {
 	SigningKeys        SigningKeys    `json:"signing_keys,omitempty"`
 	Revocations        RevocationList `json:"revocations,omitempty"`
 	DefaultPermissions Permissions    `json:"default_permissions,omitempty"`
-	Mappings           Mapping        `json:"mappings,omitempty"`
 	Info
 	GenericFields
 }
@@ -141,7 +102,6 @@ func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
 	a.Exports.Validate(vr)
 	a.Limits.Validate(vr)
 	a.DefaultPermissions.Validate(vr)
-	a.Mappings.Validate(vr)
 
 	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")
@@ -190,7 +150,6 @@ func NewAccountClaims(subject string) *AccountClaims {
 		AccountLimits{NoLimit, NoLimit, true, NoLimit, NoLimit},
 		JetStreamLimits{0, 0, 0, 0}}
 	c.Subject = subject
-	c.Mappings = Mapping{}
 	return c
 }
 
diff --git a/vendor/github.com/nats-io/jwt/v2/exports.go b/vendor/github.com/nats-io/jwt/v2/exports.go
index 852ffae3..23065fbc 100644
--- a/vendor/github.com/nats-io/jwt/v2/exports.go
+++ b/vendor/github.com/nats-io/jwt/v2/exports.go
@@ -118,7 +118,6 @@ type Export struct {
 	ResponseThreshold    time.Duration   `json:"response_threshold,omitempty"`
 	Latency              *ServiceLatency `json:"service_latency,omitempty"`
 	AccountTokenPosition uint            `json:"account_token_position,omitempty"`
-	Advertise            bool            `json:"advertise,omitempty"`
 	Info
 }
 
diff --git a/vendor/github.com/nats-io/jwt/v2/imports.go b/vendor/github.com/nats-io/jwt/v2/imports.go
index 705c630f..8eb5396c 100644
--- a/vendor/github.com/nats-io/jwt/v2/imports.go
+++ b/vendor/github.com/nats-io/jwt/v2/imports.go
@@ -15,6 +15,13 @@
 
 package jwt
 
+import (
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"time"
+)
+
 // Import describes a mapping from another account into this one
 type Import struct {
 	Name string `json:"name,omitempty"`
@@ -85,10 +92,32 @@ func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
 	var act *ActivationClaims
 
 	if i.Token != "" {
-		var err error
-		act, err = DecodeActivationClaims(i.Token)
-		if err != nil {
-			vr.AddWarning("import %q contains an invalid activation token", i.Subject)
+		// Check to see if its an embedded JWT or a URL.
+		if u, err := url.Parse(i.Token); err == nil && u.Scheme != "" {
+			c := &http.Client{Timeout: 5 * time.Second}
+			resp, err := c.Get(u.String())
+			if err != nil {
+				vr.AddWarning("import %s contains an unreachable token URL %q", i.Subject, i.Token)
+			}
+
+			if resp != nil {
+				defer resp.Body.Close()
+				body, err := ioutil.ReadAll(resp.Body)
+				if err != nil {
+					vr.AddWarning("import %s contains an unreadable token URL %q", i.Subject, i.Token)
+				} else {
+					act, err = DecodeActivationClaims(string(body))
+					if err != nil {
+						vr.AddWarning("import %s contains a URL %q with an invalid activation token", i.Subject, i.Token)
+					}
+				}
+			}
+		} else {
+			var err error
+			act, err = DecodeActivationClaims(i.Token)
+			if err != nil {
+				vr.AddWarning("import %q contains an invalid activation token", i.Subject)
+			}
 		}
 	}
 
diff --git a/vendor/github.com/nats-io/jwt/v2/operator_claims.go b/vendor/github.com/nats-io/jwt/v2/operator_claims.go
index f806002d..61d474e5 100644
--- a/vendor/github.com/nats-io/jwt/v2/operator_claims.go
+++ b/vendor/github.com/nats-io/jwt/v2/operator_claims.go
@@ -166,7 +166,6 @@ func NewOperatorClaims(subject string) *OperatorClaims {
 	}
 	c := &OperatorClaims{}
 	c.Subject = subject
-	c.Issuer = subject
 	return c
 }
 
@@ -177,10 +176,7 @@ func (oc *OperatorClaims) DidSign(op Claims) bool {
 	}
 	issuer := op.Claims().Issuer
 	if issuer == oc.Subject {
-		if !oc.StrictSigningKeyUsage {
-			return true
-		}
-		return op.Claims().Subject == oc.Subject
+		return !oc.StrictSigningKeyUsage
 	}
 	return oc.SigningKeys.Contains(issuer)
 }
diff --git a/vendor/github.com/nats-io/jwt/v2/types.go b/vendor/github.com/nats-io/jwt/v2/types.go
index b538b62a..3aa4f3a5 100644
--- a/vendor/github.com/nats-io/jwt/v2/types.go
+++ b/vendor/github.com/nats-io/jwt/v2/types.go
@@ -26,7 +26,7 @@ import (
 	"time"
 )
 
-const MaxInfoLength = 8 * 1024
+const MaxInfoLength = 255
 
 type Info struct {
 	Description string `json:"description,omitempty"`
diff --git a/vendor/modules.txt b/vendor/modules.txt
index bd2271e8..af89062a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -4,7 +4,7 @@ github.com/klauspost/compress/s2
 # github.com/minio/highwayhash v1.0.0
 ## explicit
 github.com/minio/highwayhash
-# github.com/nats-io/jwt/v2 v2.0.0-20210208203759-ff814ca5f813
+# github.com/nats-io/jwt/v2 v2.0.0-20210125223648-1c24d462becc
 ## explicit
 github.com/nats-io/jwt/v2
 # github.com/nats-io/nats.go v1.10.1-0.20210127212649-5b4924938a9a


Test patch:
diff --git a/server/accounts_test.go b/server/accounts_test.go
index 3bc5eef8..0d99f02b 100644
--- a/server/accounts_test.go
+++ b/server/accounts_test.go
@@ -22,7 +22,6 @@ import (
 	"strconv"
 	"strings"
 	"sync"
-	"sync/atomic"
 	"testing"
 	"time"
 
@@ -118,20 +117,14 @@ func TestAccountIsolationExportImport(t *testing.T) {
 			pubSubj: 0,
 			"fizz":  0,
 		}
-		count := int32(0)
-		ch := make(chan struct{}, 1)
-		if _, err := ncImp.Subscribe(">", func(m *nats.Msg) {
+		sub, err := ncImp.Subscribe(">", func(m *nats.Msg) {
 			gotSubjs[m.Subject] += 1
-			if n := atomic.AddInt32(&count, 1); n == 3 {
-				ch <- struct{}{}
-			}
-		}); err != nil {
-			t.Fatalf("Error on subscribe: %v", err)
+		})
+		if err != nil {
+			t.Fatal(err)
 		}
-		// Since both prod and cons use same server, flushing here will ensure
-		// that the interest is registered and known at the time we publish.
-		ncImp.Flush()
 
+		time.Sleep(1 * time.Second)
 		if err := ncExp.Publish(pubSubj, []byte(fmt.Sprintf("ncExp pub %s", pubSubj))); err != nil {
 			t.Fatal(err)
 		}
@@ -145,6 +138,10 @@ func TestAccountIsolationExportImport(t *testing.T) {
 		if err := ncImp.Publish("fizz", []byte("ncImp pub fizz")); err != nil {
 			t.Fatal(err)
 		}
+		time.Sleep(1 * time.Second)
+		if err := sub.Unsubscribe(); err != nil {
+			t.Fatal(err)
+		}
 
 		wantSubjs := map[string]int{
 			// Subscriber ncImp should receive publishes from ncExp and ncImp.
@@ -152,16 +149,6 @@ func TestAccountIsolationExportImport(t *testing.T) {
 			// Subscriber ncImp should only receive the publish from ncImp.
 			"fizz": 1,
 		}
-
-		// Wait for at least the 3 expected messages
-		select {
-		case <-ch:
-		case <-time.After(time.Second):
-			t.Fatalf("Expected 3 messages, got %v", atomic.LoadInt32(&count))
-		}
-		// But now wait a bit to see if subscription receives more than expected.
-		time.Sleep(50 * time.Millisecond)
-
 		if got, want := len(gotSubjs), len(wantSubjs); got != want {
 			t.Fatalf("unexpected subjs len, got=%d; want=%d", got, want)
 		}
@@ -3266,3 +3253,29 @@ func TestSubjectTransforms(t *testing.T) {
 	shouldMatch("baz.>", "foo.bar.>", "baz.1.2.3", "foo.bar.1.2.3")
 	shouldMatch("*", "foo.bar.$1", "foo", "foo.bar.foo")
 }
+
+func TestAccountSystemPermsWithGlobalAccess(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		accounts {
+			$SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] }
+		}
+	`))
+	defer os.Remove(conf)
+
+	s, _ := RunServerWithConfig(conf)
+
+	// Make sure we can connect with no auth to global account as normal.
+	nc, err := nats.Connect(s.ClientURL())
+	if err != nil {
+		t.Fatalf("Failed to create client: %v", err)
+	}
+	defer nc.Close()
+
+	// Make sure we can connect to the system account with correct credentials.
+	sc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
+	if err != nil {
+		t.Fatalf("Failed to create system client: %v", err)
+	}
+	defer sc.Close()
+}
diff --git a/server/events_test.go b/server/events_test.go
index 1bd0a4f1..8c6f2d2e 100644
--- a/server/events_test.go
+++ b/server/events_test.go
@@ -1933,7 +1933,7 @@ func TestServerEventsPingMonitorz(t *testing.T) {
 		respField []string
 	}{
 		{"VARZ", nil, &Varz{},
-			[]string{"now", "cpu", "system_account"}},
+			[]string{"now", "cpu"}},
 		{"SUBSZ", nil, &Subsz{},
 			[]string{"num_subscriptions", "num_cache"}},
 		{"CONNZ", nil, &Connz{},
diff --git a/server/jetstream_cluster_test.go b/server/jetstream_cluster_test.go
index 3bc3f1ce..153115a0 100644
--- a/server/jetstream_cluster_test.go
+++ b/server/jetstream_cluster_test.go
@@ -3568,6 +3568,38 @@ func TestJetStreamClusterQueueSubConsumer(t *testing.T) {
 	}
 }
 
+func TestJetStreamClusterLeaderStepdown(t *testing.T) {
+	c := createJetStreamClusterExplicit(t, "JSC", 3)
+	defer c.shutdown()
+
+	c.waitOnLeader()
+	cl := c.leader()
+	// Now ask the system account to have the leader stepdown.
+	s := c.randomNonLeader()
+	nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
+	if err != nil {
+		t.Fatalf("Failed to create system client: %v", err)
+	}
+	defer nc.Close()
+
+	resp, err := nc.Request(JSApiLeaderStepDown, nil, time.Second)
+	if err != nil {
+		t.Fatalf("Error on stepdown request: %v", err)
+	}
+	var sdr JSApiLeaderStepDownResponse
+	if err := json.Unmarshal(resp.Data, &sdr); err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+	if sdr.Error != nil || !sdr.Success {
+		t.Fatalf("Unexpected error for leader stepdown: %+v", sdr.Error)
+	}
+
+	c.waitOnLeader()
+	if cl == c.leader() {
+		t.Fatalf("Expected a new metaleader, got same")
+	}
+}
+
 // Support functions
 
 // Used to setup superclusters for tests.
@@ -3595,6 +3627,9 @@ var jsClusterTempl = `
 		listen: 127.0.0.1:%d
 		routes = [%s]
 	}
+
+	# For access to system account.
+	accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
 `
 
 var jsSuperClusterTempl = `
@@ -3692,7 +3727,6 @@ func createJetStreamSuperCluster(t *testing.T, numServersPer, numClusters int) *
 				return nil
 			}
 		}
-		fmt.Printf("AP is %+v\n", peers)
 		return fmt.Errorf("Not correct number of peers, expected %d, got %d", numClusters*numServersPer, len(peers))
 	})
 
diff --git a/server/jwt_test.go b/server/jwt_test.go
index 3bf0f5ac..a78e7313 100644
--- a/server/jwt_test.go
+++ b/server/jwt_test.go
@@ -5429,66 +5429,3 @@ func TestJWTAccountProtectedImport(t *testing.T) {
 		require_True(t, len(msgChan) == 0)
 	})
 }
-
-func TestJWTMappings(t *testing.T) {
-	sysKp, syspub := createKey(t)
-	sysJwt := encodeClaim(t, jwt.NewAccountClaims(syspub), syspub)
-	sysCreds := newUser(t, sysKp)
-	defer os.Remove(sysCreds)
-
-	// create two jwt, one with and one without mapping
-	aKp, aPub := createKey(t)
-	aClaim := jwt.NewAccountClaims(aPub)
-	aJwtNoM := encodeClaim(t, aClaim, aPub)
-	aClaim.AddMapping("foo1", jwt.WeightedMapping{Subject: "bar1"})
-	aJwtMap1 := encodeClaim(t, aClaim, aPub)
-
-	aClaim.Mappings = map[jwt.Subject][]jwt.WeightedMapping{}
-	aClaim.AddMapping("foo2", jwt.WeightedMapping{Subject: "bar2"})
-	aJwtMap2 := encodeClaim(t, aClaim, aPub)
-
-	dirSrv := createDir(t, "srv")
-	defer os.RemoveAll(dirSrv)
-	conf := createConfFile(t, []byte(fmt.Sprintf(`
-		listen: -1
-		operator: %s
-		system_account: %s
-		resolver: {
-			type: full
-			dir: %s
-		}
-    `, ojwt, syspub, dirSrv)))
-	defer os.Remove(conf)
-	srv, _ := RunServerWithConfig(conf)
-	defer srv.Shutdown()
-	updateJwt(t, srv.ClientURL(), sysCreds, sysJwt, 1) // update system account jwt
-
-	test := func(pub, sub string, fail bool) {
-		t.Helper()
-		nc := natsConnect(t, srv.ClientURL(), createUserCreds(t, srv, aKp))
-		defer nc.Close()
-		s, err := nc.SubscribeSync(sub)
-		require_NoError(t, err)
-		nc.Flush()
-		err = nc.Publish(pub, nil)
-		require_NoError(t, err)
-		_, err = s.NextMsg(500 * time.Millisecond)
-		switch {
-		case fail && err == nil:
-			t.Fatal("expected error, got none")
-		case !fail && err != nil:
-			t.Fatalf("expected no error, got %v", err)
-		}
-	}
-
-	// turn mappings on
-	require_Len(t, 1, updateJwt(t, srv.ClientURL(), sysCreds, aJwtMap1, 1))
-	test("foo1", "bar1", false)
-	// alter mappings
-	require_Len(t, 1, updateJwt(t, srv.ClientURL(), sysCreds, aJwtMap2, 1))
-	test("foo1", "bar1", true)
-	test("foo2", "bar2", false)
-	// turn mappings off
-	require_Len(t, 1, updateJwt(t, srv.ClientURL(), sysCreds, aJwtNoM, 1))
-	test("foo2", "bar2", true)
-}
diff --git a/server/monitor_test.go b/server/monitor_test.go
index b32f008c..d2c2932f 100644
--- a/server/monitor_test.go
+++ b/server/monitor_test.go
@@ -3783,8 +3783,6 @@ func TestMonitorAccountz(t *testing.T) {
 		t.Fatalf("Body missing value. Contains: %s", body)
 	} else if !strings.Contains(body, `"accounts": [`) {
 		t.Fatalf("Body missing value. Contains: %s", body)
-	} else if !strings.Contains(body, `"system_account": "$SYS"`) {
-		t.Fatalf("Body missing value. Contains: %s", body)
 	}
 	body = string(readBody(t, fmt.Sprintf("http://127.0.0.1:%d/accountz?acc=$SYS", s.MonitorAddr().Port)))
 	if !strings.Contains(body, `"account_detail": {`) {
@@ -3793,10 +3791,6 @@ func TestMonitorAccountz(t *testing.T) {
 		t.Fatalf("Body missing value. Contains: %s", body)
 	} else if !strings.Contains(body, `"subscriptions": 36,`) {
 		t.Fatalf("Body missing value. Contains: %s", body)
-	} else if !strings.Contains(body, `"is_system": true,`) {
-		t.Fatalf("Body missing value. Contains: %s", body)
-	} else if !strings.Contains(body, `"system_account": "$SYS"`) {
-		t.Fatalf("Body missing value. Contains: %s", body)
 	}
 }