From bf902d9e7cc3ec5a51d434c0c284aba207ebb4ae Mon Sep 17 00:00:00 2001 From: Derek Collison Date: Fri, 26 Jul 2019 16:06:14 -0700 Subject: [PATCH] Add in user JWT support for ResponsePermissions Signed-off-by: Derek Collison --- go.mod | 5 +- go.sum | 11 +- server/accounts.go | 18 +- server/jwt_test.go | 72 +++++++ vendor/github.com/nats-io/jwt/ReleaseNotes.md | 5 + .../github.com/nats-io/jwt/account_claims.go | 38 +++- .../nats-io/jwt/activation_claims.go | 2 +- vendor/github.com/nats-io/jwt/claims.go | 8 +- .../github.com/nats-io/jwt/cluster_claims.go | 2 +- vendor/github.com/nats-io/jwt/creds_utils.go | 196 ++++++++++++++++++ vendor/github.com/nats-io/jwt/exports.go | 42 +++- vendor/github.com/nats-io/jwt/genericlaims.go | 4 +- .../github.com/nats-io/jwt/operator_claims.go | 6 +- .../nats-io/jwt/revocation_claims.go | 105 ---------- .../github.com/nats-io/jwt/revocation_list.go | 32 +++ .../github.com/nats-io/jwt/server_claims.go | 2 +- vendor/github.com/nats-io/jwt/types.go | 22 +- vendor/github.com/nats-io/jwt/user_claims.go | 4 +- .../golang.org/x/sys/windows/types_windows.go | 7 +- vendor/modules.txt | 4 +- 20 files changed, 441 insertions(+), 144 deletions(-) create mode 100644 vendor/github.com/nats-io/jwt/ReleaseNotes.md create mode 100644 vendor/github.com/nats-io/jwt/creds_utils.go delete mode 100644 vendor/github.com/nats-io/jwt/revocation_claims.go create mode 100644 vendor/github.com/nats-io/jwt/revocation_list.go diff --git a/go.mod b/go.mod index 35a37707..711fe8bf 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,10 @@ module github.com/nats-io/nats-server/v2 require ( - github.com/golang/protobuf v1.3.2 // indirect - github.com/nats-io/jwt v0.2.8 + github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3 github.com/nats-io/nats.go v1.8.1 github.com/nats-io/nkeys v0.1.0 github.com/nats-io/nuid v1.0.1 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 - golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 + golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e ) diff --git a/go.sum b/go.sum index 09bef5f2..ff847e6a 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,7 @@ -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/nats-io/jwt v0.2.8 h1:PXr0mRjPCPX4cXsdfHcsqoplrNXnKOD+g2yHoh9qy1I= -github.com/nats-io/jwt v0.2.8/go.mod h1:mQxQ0uHQ9FhEVPIcTSKwx2lqZEpXWWcCgA7R6NrWvvY= +github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3 h1:5vVSRJhjWOTv/TeJX1NUGuDYbZkfcWTu/97AHlsC02o= +github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3/go.mod h1:mQxQ0uHQ9FhEVPIcTSKwx2lqZEpXWWcCgA7R6NrWvvY= github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ= github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= -github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -16,8 +13,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49N golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ= -golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/server/accounts.go b/server/accounts.go index 574b13c7..a017cdd0 100644 --- a/server/accounts.go +++ b/server/accounts.go @@ -586,8 +586,9 @@ func (a *Account) AddStreamExport(subject string, accounts []*Account) error { func (a *Account) checkStreamImportAuthorized(account *Account, subject string, imClaim *jwt.Import) bool { // Find the subject in the exports list. a.mu.RLock() - defer a.mu.RUnlock() - return a.checkStreamImportAuthorizedNoLock(account, subject, imClaim) + auth := a.checkStreamImportAuthorizedNoLock(account, subject, imClaim) + a.mu.RUnlock() + return auth } func (a *Account) checkStreamImportAuthorizedNoLock(account *Account, subject string, imClaim *jwt.Import) bool { @@ -1165,6 +1166,19 @@ func buildInternalNkeyUser(uc *jwt.UserClaims, acc *Account) *NkeyUser { p.Subscribe.Allow = uc.Sub.Allow p.Subscribe.Deny = uc.Sub.Deny } + if uc.Resp != nil { + if p == nil { + p = &Permissions{Publish: &SubjectPermission{}} + } + if p.Publish.Allow == nil { + // We turn off the blanket allow statement. + p.Publish.Allow = []string{} + } + p.Response = &ResponsePermission{ + MaxMsgs: uc.Resp.MaxMsgs, + Expires: uc.Resp.Expires, + } + } nu.Permissions = p return nu } diff --git a/server/jwt_test.go b/server/jwt_test.go index 50c21904..87f87439 100644 --- a/server/jwt_test.go +++ b/server/jwt_test.go @@ -370,6 +370,78 @@ func TestJWTUserPermissionClaims(t *testing.T) { } } +func TestJWTUserResponsePermissionClaims(t *testing.T) { + okp, _ := nkeys.FromSeed(oSeed) + + nkp, _ := nkeys.CreateUser() + pub, _ := nkp.PublicKey() + nuc := jwt.NewUserClaims(pub) + nuc.Permissions.Resp = &jwt.ResponsePermission{ + MaxMsgs: 22, + Expires: 100 * time.Millisecond, + } + + akp, _ := nkeys.FromSeed(aSeed) + apub, _ := akp.PublicKey() + nac := jwt.NewAccountClaims(apub) + ajwt, err := nac.Encode(okp) + if err != nil { + t.Fatalf("Error generating account JWT: %v", err) + } + + jwt, err := nuc.Encode(akp) + if err != nil { + t.Fatalf("Error generating user JWT: %v", err) + } + + s := opTrustBasicSetup() + defer s.Shutdown() + buildMemAccResolver(s) + addAccountToMemResolver(s, apub, ajwt) + + c, cr, l := newClientForServer(s) + + // Sign Nonce + var info nonceInfo + json.Unmarshal([]byte(l[5:]), &info) + sigraw, _ := nkp.Sign([]byte(info.Nonce)) + sig := base64.RawURLEncoding.EncodeToString(sigraw) + + // PING needed to flush the +OK/-ERR to us. + // This should fail too since no account resolver is defined. + cs := fmt.Sprintf("CONNECT {\"jwt\":%q,\"sig\":\"%s\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n", jwt, sig) + go c.parse([]byte(cs)) + l, _ = cr.ReadString('\n') + if !strings.HasPrefix(l, "+OK") { + t.Fatalf("Expected an OK, got: %v", l) + } + + // Now check client to make sure permissions transferred. + c.mu.Lock() + defer c.mu.Unlock() + + if c.perms == nil { + t.Fatalf("Expected client permissions to be set") + } + if c.perms.pub.allow == nil { + t.Fatalf("Expected client perms for pub allow to be non-nil") + } + if lpa := c.perms.pub.allow.Count(); lpa != 0 { + t.Fatalf("Expected 0 publish allow subjects, got %d", lpa) + } + if c.perms.resp == nil { + t.Fatalf("Expected client perms for response permissions to be non-nil") + } + if c.perms.resp.MaxMsgs != nuc.Permissions.Resp.MaxMsgs { + t.Fatalf("Expected client perms for response permissions MaxMsgs to be same as jwt: %d vs %d", + c.perms.resp.MaxMsgs, nuc.Permissions.Resp.MaxMsgs) + } + if c.perms.resp.Expires != nuc.Permissions.Resp.Expires { + t.Fatalf("Expected client perms for response permissions Expires to be same as jwt: %v vs %v", + c.perms.resp.Expires, nuc.Permissions.Resp.Expires) + } +} + func TestJWTAccountExpired(t *testing.T) { s := opTrustBasicSetup() defer s.Shutdown() diff --git a/vendor/github.com/nats-io/jwt/ReleaseNotes.md b/vendor/github.com/nats-io/jwt/ReleaseNotes.md new file mode 100644 index 00000000..500965ea --- /dev/null +++ b/vendor/github.com/nats-io/jwt/ReleaseNotes.md @@ -0,0 +1,5 @@ +# Release Notes + +## 0.3.0 + +* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims. diff --git a/vendor/github.com/nats-io/jwt/account_claims.go b/vendor/github.com/nats-io/jwt/account_claims.go index 04a2f6ac..ec7132d0 100644 --- a/vendor/github.com/nats-io/jwt/account_claims.go +++ b/vendor/github.com/nats-io/jwt/account_claims.go @@ -17,11 +17,12 @@ package jwt import ( "errors" + "time" "github.com/nats-io/nkeys" ) -// Signifies no limit. +// NoLimit is used to indicate a limit field is unlimited in value. const NoLimit = -1 // OperatorLimits are used to limit access by an account @@ -58,6 +59,7 @@ type Account struct { Identities []Identity `json:"identity,omitempty"` Limits OperatorLimits `json:"limits,omitempty"` SigningKeys StringList `json:"signing_keys,omitempty"` + Revocations RevocationList `json:"revocations,omitempty"` } // Validate checks if the account is valid, based on the wrapper @@ -127,7 +129,7 @@ func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) { } a.ClaimsData.Type = AccountClaim - return a.ClaimsData.encode(pair, a) + return a.ClaimsData.Encode(pair, a) } // DecodeAccountClaims decodes account claims from a JWT string @@ -184,3 +186,35 @@ func (a *AccountClaims) DidSign(op Claims) bool { } return false } + +// Revoke enters a revocation by publickey using time.Now(). +func (a *AccountClaims) Revoke(pubKey string) { + a.RevokeAt(pubKey, time.Now()) +} + +// RevokeAt enters a revocation by publickey and timestamp into this export +// If there is already a revocation for this public key that is newer, it is kept. +func (a *AccountClaims) RevokeAt(pubKey string, timestamp time.Time) { + if a.Revocations == nil { + a.Revocations = RevocationList{} + } + + a.Revocations.Revoke(pubKey, timestamp) +} + +// ClearRevocation removes any revocation for the public key +func (a *AccountClaims) ClearRevocation(pubKey string) { + a.Revocations.ClearRevocation(pubKey) +} + +// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than +// the one passed in. Generally this method is called with time.Now() but other time's can +// be used for testing. +func (a *AccountClaims) IsRevokedAt(pubKey string, timestamp time.Time) bool { + return a.Revocations.IsRevoked(pubKey, timestamp) +} + +// IsRevoked checks if the public key is in the revoked list with time.Now() +func (a *AccountClaims) IsRevoked(pubKey string) bool { + return a.Revocations.IsRevoked(pubKey, time.Now()) +} diff --git a/vendor/github.com/nats-io/jwt/activation_claims.go b/vendor/github.com/nats-io/jwt/activation_claims.go index ebc7b7a8..99228a75 100644 --- a/vendor/github.com/nats-io/jwt/activation_claims.go +++ b/vendor/github.com/nats-io/jwt/activation_claims.go @@ -83,7 +83,7 @@ func (a *ActivationClaims) Encode(pair nkeys.KeyPair) (string, error) { return "", errors.New("expected subject to be an account") } a.ClaimsData.Type = ActivationClaim - return a.ClaimsData.encode(pair, a) + return a.ClaimsData.Encode(pair, a) } // DecodeActivationClaims tries to create an activation claim from a JWT string diff --git a/vendor/github.com/nats-io/jwt/claims.go b/vendor/github.com/nats-io/jwt/claims.go index 45f00dad..d402bcc5 100644 --- a/vendor/github.com/nats-io/jwt/claims.go +++ b/vendor/github.com/nats-io/jwt/claims.go @@ -44,8 +44,6 @@ const ( ClusterClaim = "cluster" //OperatorClaim is the type of an operator JWT OperatorClaim = "operator" - //RevocationClaim is the type of an revocation JWT - RevocationClaim = "revocation" ) // Claims is a JWT claims @@ -180,9 +178,9 @@ func (c *ClaimsData) hash() (string, error) { return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil } -// encode encodes a claim into a JWT token. The claim is signed with the +// Encode encodes a claim into a JWT token. The claim is signed with the // provided nkey's private key -func (c *ClaimsData) encode(kp nkeys.KeyPair, payload Claims) (string, error) { +func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) { return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload) } @@ -241,7 +239,7 @@ func (c *ClaimsData) IsSelfSigned() bool { // Decode takes a JWT string decodes it and validates it // and return the embedded Claims. If the token header // doesn't match the expected algorithm, or the claim is -// not valid or verification fails an error is returned +// not valid or verification fails an error is returned. func Decode(token string, target Claims) error { // must have 3 chunks chunks := strings.Split(token, ".") diff --git a/vendor/github.com/nats-io/jwt/cluster_claims.go b/vendor/github.com/nats-io/jwt/cluster_claims.go index d526ba3f..bbfcf06f 100644 --- a/vendor/github.com/nats-io/jwt/cluster_claims.go +++ b/vendor/github.com/nats-io/jwt/cluster_claims.go @@ -56,7 +56,7 @@ func (c *ClusterClaims) Encode(pair nkeys.KeyPair) (string, error) { return "", errors.New("expected subject to be a cluster public key") } c.ClaimsData.Type = ClusterClaim - return c.ClaimsData.encode(pair, c) + return c.ClaimsData.Encode(pair, c) } // DecodeClusterClaims tries to parse cluster claims from a JWT string diff --git a/vendor/github.com/nats-io/jwt/creds_utils.go b/vendor/github.com/nats-io/jwt/creds_utils.go new file mode 100644 index 00000000..72b28c9a --- /dev/null +++ b/vendor/github.com/nats-io/jwt/creds_utils.go @@ -0,0 +1,196 @@ +package jwt + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strings" + + "github.com/nats-io/nkeys" +) + +// DecorateJWT returns a decorated JWT that describes the kind of JWT +func DecorateJWT(jwtString string) ([]byte, error) { + gc, err := DecodeGeneric(jwtString) + if err != nil { + return nil, err + } + return formatJwt(string(gc.Type), jwtString) +} + +func formatJwt(kind string, jwtString string) ([]byte, error) { + templ := `-----BEGIN NATS %s JWT----- +%s +------END NATS %s JWT------ +` + w := bytes.NewBuffer(nil) + kind = strings.ToUpper(kind) + _, err := fmt.Fprintf(w, templ, kind, jwtString, kind) + if err != nil { + return nil, err + } + return w.Bytes(), nil +} + +func DecorateSeed(seed []byte) ([]byte, error) { + w := bytes.NewBuffer(nil) + ts := bytes.TrimSpace(seed) + pre := string(ts[0:2]) + kind := "" + switch pre { + case "SU": + kind = "USER" + case "SA": + kind = "ACCOUNT" + case "SO": + kind = "OPERATOR" + default: + return nil, errors.New("seed is not an operator, account or user seed") + } + header := `************************* IMPORTANT ************************* +NKEY Seed printed below can be used to sign and prove identity. +NKEYs are sensitive and should be treated as secrets. +-----BEGIN %s NKEY SEED----- +` + _, err := fmt.Fprintf(w, header, kind) + if err != nil { + return nil, err + } + w.Write(ts) + + footer := ` +------END %s NKEY SEED------ +************************************************************* +` + _, err = fmt.Fprintf(w, footer, kind) + if err != nil { + return nil, err + } + return w.Bytes(), nil +} + +var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))`) + +// An user config file looks like this: +// -----BEGIN NATS USER JWT----- +// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5... +// ------END NATS USER JWT------ +// +// ************************* IMPORTANT ************************* +// NKEY Seed printed below can be used sign and prove identity. +// NKEYs are sensitive and should be treated as secrets. +// +// -----BEGIN USER NKEY SEED----- +// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM +// ------END USER NKEY SEED------ + +// FormatUserConfig returns a decorated file with a decorated JWT and decorated seed +func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) { + gc, err := DecodeGeneric(jwtString) + if err != nil { + return nil, err + } + if gc.Type != UserClaim { + return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type)) + } + + w := bytes.NewBuffer(nil) + + jd, err := formatJwt(string(gc.Type), jwtString) + if err != nil { + return nil, err + } + _, err = w.Write(jd) + if err != nil { + return nil, err + } + if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) { + return nil, fmt.Errorf("nkey seed is not an user seed") + } + + d, err := DecorateSeed(seed) + if err != nil { + return nil, err + } + _, err = w.Write(d) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func ParseDecoratedJWT(contents []byte) (string, error) { + defer wipeSlice(contents) + + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) == 0 { + return string(contents), nil + } + // First result should be the user JWT. + // We copy here so that if the file contained a seed file too we wipe appropriately. + raw := items[0][1] + tmp := make([]byte, len(raw)) + copy(tmp, raw) + return string(tmp), nil +} + +func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) { + var seed []byte + defer wipeSlice(contents) + + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) > 1 { + seed = items[1][1] + } else { + lines := bytes.Split(contents, []byte("\n")) + for _, line := range lines { + if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { + seed = line + break + } + } + } + if seed == nil { + return nil, errors.New("no nkey seed found") + } + if !bytes.HasPrefix(seed, []byte("SO")) && + !bytes.HasPrefix(seed, []byte("SA")) && + !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain a seed nkey") + } + kp, err := nkeys.FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} + +func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) { + nk, err := ParseDecoratedNKey(contents) + if err != nil { + return nil, err + } + seed, err := nk.Seed() + if err != nil { + return nil, err + } + if !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain an user seed nkey") + } + kp, err := nkeys.FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} + +// Just wipe slice with 'x', for clearing contents of nkey seed file. +func wipeSlice(buf []byte) { + for i := range buf { + buf[i] = 'x' + } +} diff --git a/vendor/github.com/nats-io/jwt/exports.go b/vendor/github.com/nats-io/jwt/exports.go index 344b41a3..1a6c4fc9 100644 --- a/vendor/github.com/nats-io/jwt/exports.go +++ b/vendor/github.com/nats-io/jwt/exports.go @@ -17,14 +17,16 @@ package jwt import ( "fmt" + "time" ) // 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"` + 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"` } // IsService returns true if an export is for a service @@ -45,6 +47,38 @@ func (e *Export) Validate(vr *ValidationResults) { e.Subject.Validate(vr) } +// Revoke enters a revocation by publickey using time.Now(). +func (e *Export) Revoke(pubKey string) { + e.RevokeAt(pubKey, time.Now()) +} + +// RevokeAt enters a revocation by publickey and timestamp into this export +// If there is already a revocation for this public key that is newer, it is kept. +func (e *Export) RevokeAt(pubKey string, timestamp time.Time) { + if e.Revocations == nil { + e.Revocations = RevocationList{} + } + + e.Revocations.Revoke(pubKey, timestamp) +} + +// ClearRevocation removes any revocation for the public key +func (e *Export) ClearRevocation(pubKey string) { + e.Revocations.ClearRevocation(pubKey) +} + +// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than +// the one passed in. Generally this method is called with time.Now() but other time's can +// be used for testing. +func (e *Export) IsRevokedAt(pubKey string, timestamp time.Time) bool { + return e.Revocations.IsRevoked(pubKey, timestamp) +} + +// IsRevoked checks if the public key is in the revoked list with time.Now() +func (e *Export) IsRevoked(pubKey string) bool { + return e.Revocations.IsRevoked(pubKey, time.Now()) +} + // Exports is an array of exports type Exports []*Export diff --git a/vendor/github.com/nats-io/jwt/genericlaims.go b/vendor/github.com/nats-io/jwt/genericlaims.go index 7098955b..94cd86e0 100644 --- a/vendor/github.com/nats-io/jwt/genericlaims.go +++ b/vendor/github.com/nats-io/jwt/genericlaims.go @@ -48,14 +48,14 @@ func (gc *GenericClaims) Claims() *ClaimsData { return &gc.ClaimsData } -// Payload returns the custom part of the claiims data +// Payload returns the custom part of the claims data func (gc *GenericClaims) Payload() interface{} { return &gc.Data } // Encode takes a generic claims and creates a JWT string func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) { - return gc.ClaimsData.encode(pair, gc) + return gc.ClaimsData.Encode(pair, gc) } // Validate checks the generic part of the claims data diff --git a/vendor/github.com/nats-io/jwt/operator_claims.go b/vendor/github.com/nats-io/jwt/operator_claims.go index a1c1c83f..1383b7ad 100644 --- a/vendor/github.com/nats-io/jwt/operator_claims.go +++ b/vendor/github.com/nats-io/jwt/operator_claims.go @@ -73,6 +73,10 @@ func (o *Operator) validateAccountServerURL() error { // ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url. func ValidateOperatorServiceURL(v string) error { + // should be possible for the service url to not be expressed + if v == "" { + return nil + } u, err := url.Parse(v) if err != nil { return fmt.Errorf("error parsing operator service url %q: %v", v, err) @@ -152,7 +156,7 @@ func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) { return "", err } oc.ClaimsData.Type = OperatorClaim - return oc.ClaimsData.encode(pair, oc) + return oc.ClaimsData.Encode(pair, oc) } // DecodeOperatorClaims tries to create an operator claims from a JWt string diff --git a/vendor/github.com/nats-io/jwt/revocation_claims.go b/vendor/github.com/nats-io/jwt/revocation_claims.go deleted file mode 100644 index 20f4ad81..00000000 --- a/vendor/github.com/nats-io/jwt/revocation_claims.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package jwt - -import ( - "github.com/nats-io/nkeys" -) - -// Revocation defines the custom parts of a revocation JWt -type Revocation struct { - JWT string `json:"jwt,omitempty"` - Reason string `json:"reason,omitempty"` -} - -// Validate checks the JWT and reason for a revocation -func (u *Revocation) Validate(vr *ValidationResults) { - if u.JWT == "" { - vr.AddError("revocation token has no JWT to revoke") - } - - _, err := DecodeGeneric(u.JWT) - - if err != nil { - vr.AddError("revocation token has an invalid JWT") - } -} - -// RevocationClaims defines a revocation tokens data -type RevocationClaims struct { - ClaimsData - Revocation `json:"nats,omitempty"` -} - -// NewRevocationClaims creates a new revocation JWT for the specified subject/public key -func NewRevocationClaims(subject string) *RevocationClaims { - if subject == "" { - return nil - } - c := &RevocationClaims{} - c.Subject = subject - return c -} - -// Encode translates the claims to a JWT string -func (rc *RevocationClaims) Encode(pair nkeys.KeyPair) (string, error) { - rc.ClaimsData.Type = RevocationClaim - return rc.ClaimsData.encode(pair, rc) -} - -// DecodeRevocationClaims tries to parse a JWT string as a RevocationClaims -func DecodeRevocationClaims(token string) (*RevocationClaims, error) { - v := RevocationClaims{} - if err := Decode(token, &v); err != nil { - return nil, err - } - return &v, nil -} - -func (rc *RevocationClaims) String() string { - return rc.ClaimsData.String(rc) -} - -// Payload returns the revocation specific part of the claims -func (rc *RevocationClaims) Payload() interface{} { - return &rc.Revocation -} - -// Validate checks the generic and revocation parts of the claims -func (rc *RevocationClaims) Validate(vr *ValidationResults) { - rc.ClaimsData.Validate(vr) - rc.Revocation.Validate(vr) - - theJWT, err := DecodeGeneric(rc.Revocation.JWT) - if err != nil { - vr.AddError("revocation contains an invalid JWT") - return // can't do the remaining checks - } - - if theJWT.Issuer != rc.Issuer { - vr.AddError("Revocation issuer doesn't match JWT to revoke") - } -} - -// ExpectedPrefixes defines who can sign a revocation token, account or operator -func (rc *RevocationClaims) ExpectedPrefixes() []nkeys.PrefixByte { - return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteAccount} -} - -// Claims returns the generic part of the claims -func (rc *RevocationClaims) Claims() *ClaimsData { - return &rc.ClaimsData -} diff --git a/vendor/github.com/nats-io/jwt/revocation_list.go b/vendor/github.com/nats-io/jwt/revocation_list.go new file mode 100644 index 00000000..fb1d8367 --- /dev/null +++ b/vendor/github.com/nats-io/jwt/revocation_list.go @@ -0,0 +1,32 @@ +package jwt + +import ( + "time" +) + +// RevocationList is used to store a mapping of public keys to unix timestamps +type RevocationList map[string]int64 + +// Revoke enters a revocation by publickey and timestamp into this export +// If there is already a revocation for this public key that is newer, it is kept. +func (r RevocationList) Revoke(pubKey string, timestamp time.Time) { + newTS := timestamp.Unix() + if ts, ok := r[pubKey]; ok && ts > newTS { + return + } + + r[pubKey] = newTS +} + +// ClearRevocation removes any revocation for the public key +func (r RevocationList) ClearRevocation(pubKey string) { + delete(r, pubKey) +} + +// IsRevoked checks if the public key is in the revoked list with a timestamp later than +// the one passed in. Generally this method is called with time.Now() but other time's can +// be used for testing. +func (r RevocationList) IsRevoked(pubKey string, timestamp time.Time) bool { + ts, ok := r[pubKey] + return ok && ts > timestamp.Unix() +} diff --git a/vendor/github.com/nats-io/jwt/server_claims.go b/vendor/github.com/nats-io/jwt/server_claims.go index 12f6dd71..c18f167f 100644 --- a/vendor/github.com/nats-io/jwt/server_claims.go +++ b/vendor/github.com/nats-io/jwt/server_claims.go @@ -56,7 +56,7 @@ func (s *ServerClaims) Encode(pair nkeys.KeyPair) (string, error) { return "", errors.New("expected subject to be a server public key") } s.ClaimsData.Type = ServerClaim - return s.ClaimsData.encode(pair, s) + return s.ClaimsData.Encode(pair, s) } // DecodeServerClaims tries to parse server claims from a JWT string diff --git a/vendor/github.com/nats-io/jwt/types.go b/vendor/github.com/nats-io/jwt/types.go index e3145263..a1f09fd9 100644 --- a/vendor/github.com/nats-io/jwt/types.go +++ b/vendor/github.com/nats-io/jwt/types.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 The NATS Authors + * Copyright 2018-2019 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 @@ -220,16 +220,32 @@ func (p *Permission) Validate(vr *ValidationResults) { } } +// ResponsePermission can be used to allow responses to any reply subject +// that is received on a valid subscription. +type ResponsePermission struct { + MaxMsgs int `json:"max"` + Expires time.Duration `json:"ttl"` +} + +// Validate the response permission. +func (p *ResponsePermission) Validate(vr *ValidationResults) { + // Any values can be valid for now. +} + // Permissions are used to restrict subject access, either on a user or for everyone on a server by default type Permissions struct { - Pub Permission `json:"pub,omitempty"` - Sub Permission `json:"sub,omitempty"` + Pub Permission `json:"pub,omitempty"` + Sub Permission `json:"sub,omitempty"` + Resp *ResponsePermission `json:"resp,omitempty"` } // Validate the pub and sub fields in the permissions list func (p *Permissions) Validate(vr *ValidationResults) { p.Pub.Validate(vr) p.Sub.Validate(vr) + if p.Resp != nil { + p.Resp.Validate(vr) + } } // StringList is a wrapper for an array of strings diff --git a/vendor/github.com/nats-io/jwt/user_claims.go b/vendor/github.com/nats-io/jwt/user_claims.go index 8c64d5c2..0ec1da3f 100644 --- a/vendor/github.com/nats-io/jwt/user_claims.go +++ b/vendor/github.com/nats-io/jwt/user_claims.go @@ -1,5 +1,5 @@ /* - * Copyright 2018 The NATS Authors + * Copyright 2018-2019 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 @@ -58,7 +58,7 @@ func (u *UserClaims) Encode(pair nkeys.KeyPair) (string, error) { return "", errors.New("expected subject to be user public key") } u.ClaimsData.Type = UserClaim - return u.ClaimsData.encode(pair, u) + return u.ClaimsData.Encode(pair, u) } // DecodeUserClaims tries to parse a user claims from a JWT string diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 8a563f92..1e3947f0 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -197,8 +197,11 @@ const ( FILE_MAP_READ = 0x04 FILE_MAP_EXECUTE = 0x20 - CTRL_C_EVENT = 0 - CTRL_BREAK_EVENT = 1 + CTRL_C_EVENT = 0 + CTRL_BREAK_EVENT = 1 + CTRL_CLOSE_EVENT = 2 + CTRL_LOGOFF_EVENT = 5 + CTRL_SHUTDOWN_EVENT = 6 // Windows reserves errors >= 1<<29 for application use. APPLICATION_ERROR = 1 << 29 diff --git a/vendor/modules.txt b/vendor/modules.txt index bba43d97..61cad24c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/nats-io/jwt v0.2.8 +# github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3 github.com/nats-io/jwt # github.com/nats-io/nats.go v1.8.1 github.com/nats-io/nats.go @@ -13,7 +13,7 @@ golang.org/x/crypto/bcrypt golang.org/x/crypto/ed25519 golang.org/x/crypto/blowfish golang.org/x/crypto/ed25519/internal/edwards25519 -# golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 +# golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e golang.org/x/sys/windows/svc/eventlog golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/debug