mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-16 11:04:42 -07:00
Merge pull request #1085 from nats-io/reponse
Add in user JWT support for ResponsePermissions
This commit is contained in:
5
go.mod
5
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
|
||||
)
|
||||
|
||||
11
go.sum
11
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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
5
vendor/github.com/nats-io/jwt/ReleaseNotes.md
generated
vendored
Normal file
5
vendor/github.com/nats-io/jwt/ReleaseNotes.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Release Notes
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims.
|
||||
38
vendor/github.com/nats-io/jwt/account_claims.go
generated
vendored
38
vendor/github.com/nats-io/jwt/account_claims.go
generated
vendored
@@ -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())
|
||||
}
|
||||
|
||||
2
vendor/github.com/nats-io/jwt/activation_claims.go
generated
vendored
2
vendor/github.com/nats-io/jwt/activation_claims.go
generated
vendored
@@ -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
|
||||
|
||||
8
vendor/github.com/nats-io/jwt/claims.go
generated
vendored
8
vendor/github.com/nats-io/jwt/claims.go
generated
vendored
@@ -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, ".")
|
||||
|
||||
2
vendor/github.com/nats-io/jwt/cluster_claims.go
generated
vendored
2
vendor/github.com/nats-io/jwt/cluster_claims.go
generated
vendored
@@ -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
|
||||
|
||||
196
vendor/github.com/nats-io/jwt/creds_utils.go
generated
vendored
Normal file
196
vendor/github.com/nats-io/jwt/creds_utils.go
generated
vendored
Normal file
@@ -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'
|
||||
}
|
||||
}
|
||||
42
vendor/github.com/nats-io/jwt/exports.go
generated
vendored
42
vendor/github.com/nats-io/jwt/exports.go
generated
vendored
@@ -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
|
||||
|
||||
|
||||
4
vendor/github.com/nats-io/jwt/genericlaims.go
generated
vendored
4
vendor/github.com/nats-io/jwt/genericlaims.go
generated
vendored
@@ -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
|
||||
|
||||
6
vendor/github.com/nats-io/jwt/operator_claims.go
generated
vendored
6
vendor/github.com/nats-io/jwt/operator_claims.go
generated
vendored
@@ -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
|
||||
|
||||
105
vendor/github.com/nats-io/jwt/revocation_claims.go
generated
vendored
105
vendor/github.com/nats-io/jwt/revocation_claims.go
generated
vendored
@@ -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
|
||||
}
|
||||
32
vendor/github.com/nats-io/jwt/revocation_list.go
generated
vendored
Normal file
32
vendor/github.com/nats-io/jwt/revocation_list.go
generated
vendored
Normal file
@@ -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()
|
||||
}
|
||||
2
vendor/github.com/nats-io/jwt/server_claims.go
generated
vendored
2
vendor/github.com/nats-io/jwt/server_claims.go
generated
vendored
@@ -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
|
||||
|
||||
22
vendor/github.com/nats-io/jwt/types.go
generated
vendored
22
vendor/github.com/nats-io/jwt/types.go
generated
vendored
@@ -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
|
||||
|
||||
4
vendor/github.com/nats-io/jwt/user_claims.go
generated
vendored
4
vendor/github.com/nats-io/jwt/user_claims.go
generated
vendored
@@ -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
|
||||
|
||||
7
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
7
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
@@ -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
|
||||
|
||||
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user