mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-17 03:24:40 -07:00
Merge pull request #1091 from nats-io/jwt_response_type
Added support for service response types
This commit is contained in:
2
go.mod
2
go.mod
@@ -1,7 +1,7 @@
|
||||
module github.com/nats-io/nats-server/v2
|
||||
|
||||
require (
|
||||
github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3
|
||||
github.com/nats-io/jwt v0.2.13-0.20190807221443-88776a07123f
|
||||
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
|
||||
|
||||
3
go.sum
3
go.sum
@@ -1,5 +1,7 @@
|
||||
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/jwt v0.2.13-0.20190807221443-88776a07123f h1:HXbsDIrce5ay5anz4HAI57VYDcGznOYs0hCj2QZXBzI=
|
||||
github.com/nats-io/jwt v0.2.13-0.20190807221443-88776a07123f/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/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
|
||||
@@ -14,5 +16,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
|
||||
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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
|
||||
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=
|
||||
|
||||
@@ -1200,7 +1200,14 @@ func (s *Server) updateAccountClaims(a *Account, ac *jwt.AccountClaims) {
|
||||
}
|
||||
case jwt.Service:
|
||||
s.Debugf("Adding service export %q for %s", e.Subject, a.Name)
|
||||
if err := a.AddServiceExport(string(e.Subject), authAccounts(e.TokenReq)); err != nil {
|
||||
rt := Singelton
|
||||
switch e.ResponseType {
|
||||
case jwt.ResponseTypeStream:
|
||||
rt = Stream
|
||||
case jwt.ResponseTypeChunked:
|
||||
rt = Chunked
|
||||
}
|
||||
if err := a.AddServiceExportWithResponse(string(e.Subject), rt, authAccounts(e.TokenReq)); err != nil {
|
||||
s.Debugf("Error adding service export to account [%s]: %v", a.Name, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,6 +835,94 @@ func TestJWTAccountBasicImportExport(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTAccountExportWithResponseType(t *testing.T) {
|
||||
s := opTrustBasicSetup()
|
||||
defer s.Shutdown()
|
||||
buildMemAccResolver(s)
|
||||
|
||||
okp, _ := nkeys.FromSeed(oSeed)
|
||||
|
||||
// Create accounts and imports/exports.
|
||||
fooKP, _ := nkeys.CreateAccount()
|
||||
fooPub, _ := fooKP.PublicKey()
|
||||
fooAC := jwt.NewAccountClaims(fooPub)
|
||||
|
||||
// Now create Exports.
|
||||
serviceStreamExport := &jwt.Export{Subject: "test.stream", Type: jwt.Service, ResponseType: jwt.ResponseTypeStream, TokenReq: false}
|
||||
serviceChunkExport := &jwt.Export{Subject: "test.chunk", Type: jwt.Service, ResponseType: jwt.ResponseTypeChunked, TokenReq: false}
|
||||
serviceSingletonExport := &jwt.Export{Subject: "test.single", Type: jwt.Service, ResponseType: jwt.ResponseTypeSingleton, TokenReq: true}
|
||||
serviceDefExport := &jwt.Export{Subject: "test.def", Type: jwt.Service, TokenReq: true}
|
||||
serviceOldExport := &jwt.Export{Subject: "test.old", Type: jwt.Service, TokenReq: false}
|
||||
|
||||
fooAC.Exports.Add(serviceStreamExport, serviceSingletonExport, serviceChunkExport, serviceDefExport, serviceOldExport)
|
||||
fooJWT, err := fooAC.Encode(okp)
|
||||
if err != nil {
|
||||
t.Fatalf("Error generating account JWT: %v", err)
|
||||
}
|
||||
|
||||
addAccountToMemResolver(s, fooPub, fooJWT)
|
||||
|
||||
fooAcc, _ := s.LookupAccount(fooPub)
|
||||
if fooAcc == nil {
|
||||
t.Fatalf("Expected to retrieve the account")
|
||||
}
|
||||
|
||||
services := fooAcc.exports.services
|
||||
|
||||
if len(services) != 5 {
|
||||
t.Fatalf("Expected 4 services")
|
||||
}
|
||||
|
||||
se, ok := services["test.stream"]
|
||||
if !ok || se == nil {
|
||||
t.Fatalf("Expected to map a service export")
|
||||
}
|
||||
if se.tokenReq {
|
||||
t.Fatalf("Expected the service export to not require tokens")
|
||||
}
|
||||
if se.respType != Stream {
|
||||
t.Fatalf("Expected the service export to respond with a stream")
|
||||
}
|
||||
|
||||
se, ok = services["test.chunk"]
|
||||
if !ok || se == nil {
|
||||
t.Fatalf("Expected to map a service export")
|
||||
}
|
||||
if se.tokenReq {
|
||||
t.Fatalf("Expected the service export to not require tokens")
|
||||
}
|
||||
if se.respType != Chunked {
|
||||
t.Fatalf("Expected the service export to respond with a stream")
|
||||
}
|
||||
|
||||
se, ok = services["test.def"]
|
||||
if !ok || se == nil {
|
||||
t.Fatalf("Expected to map a service export")
|
||||
}
|
||||
if !se.tokenReq {
|
||||
t.Fatalf("Expected the service export to not require tokens")
|
||||
}
|
||||
if se.respType != Singelton {
|
||||
t.Fatalf("Expected the service export to respond with a stream")
|
||||
}
|
||||
|
||||
se, ok = services["test.single"]
|
||||
if !ok || se == nil {
|
||||
t.Fatalf("Expected to map a service export")
|
||||
}
|
||||
if !se.tokenReq {
|
||||
t.Fatalf("Expected the service export to not require tokens")
|
||||
}
|
||||
if se.respType != Singelton {
|
||||
t.Fatalf("Expected the service export to respond with a stream")
|
||||
}
|
||||
|
||||
se, ok = services["test.old"]
|
||||
if !ok || se != nil {
|
||||
t.Fatalf("Service with a singleton response and no tokens should be nil in the map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTAccountImportExportUpdates(t *testing.T) {
|
||||
s := opTrustBasicSetup()
|
||||
defer s.Shutdown()
|
||||
|
||||
14
vendor/github.com/nats-io/jwt/creds_utils.go
generated
vendored
14
vendor/github.com/nats-io/jwt/creds_utils.go
generated
vendored
@@ -33,6 +33,15 @@ func formatJwt(kind string, jwtString string) ([]byte, error) {
|
||||
return w.Bytes(), nil
|
||||
}
|
||||
|
||||
// DecorateSeed takes a seed and returns a string that wraps
|
||||
// the seed in the form:
|
||||
// ************************* 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------
|
||||
func DecorateSeed(seed []byte) ([]byte, error) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
ts := bytes.TrimSpace(seed)
|
||||
@@ -121,6 +130,7 @@ func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) {
|
||||
return w.Bytes(), nil
|
||||
}
|
||||
|
||||
// ParseDecoratedJWT takes a creds file and returns the JWT portion.
|
||||
func ParseDecoratedJWT(contents []byte) (string, error) {
|
||||
defer wipeSlice(contents)
|
||||
|
||||
@@ -136,6 +146,8 @@ func ParseDecoratedJWT(contents []byte) (string, error) {
|
||||
return string(tmp), nil
|
||||
}
|
||||
|
||||
// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a
|
||||
// key pair from it.
|
||||
func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) {
|
||||
var seed []byte
|
||||
defer wipeSlice(contents)
|
||||
@@ -169,6 +181,8 @@ func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) {
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a
|
||||
// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys.
|
||||
func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) {
|
||||
nk, err := ParseDecoratedNKey(contents)
|
||||
if err != nil {
|
||||
|
||||
47
vendor/github.com/nats-io/jwt/exports.go
generated
vendored
47
vendor/github.com/nats-io/jwt/exports.go
generated
vendored
@@ -20,13 +20,28 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ResponseType is used to store an export response type
|
||||
type ResponseType string
|
||||
|
||||
const (
|
||||
// ResponseTypeSingleton is used for a service that sends a single response only
|
||||
ResponseTypeSingleton = "Singleton"
|
||||
|
||||
// ResponseTypeStream is used for a service that will send multiple responses
|
||||
ResponseTypeStream = "Stream"
|
||||
|
||||
// ResponseTypeChunked is used for a service that sends a single response in chunks (so not quite a stream)
|
||||
ResponseTypeChunked = "Chunked"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
Revocations RevocationList `json:"revocations,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"`
|
||||
ResponseType ResponseType `json:"response_type,omitempty"`
|
||||
}
|
||||
|
||||
// IsService returns true if an export is for a service
|
||||
@@ -39,11 +54,33 @@ func (e *Export) IsStream() bool {
|
||||
return e.Type == Stream
|
||||
}
|
||||
|
||||
// IsSingleResponse returns true if an export has a single response
|
||||
// or no resopnse type is set, also checks that the type is service
|
||||
func (e *Export) IsSingleResponse() bool {
|
||||
return e.Type == Service && (e.ResponseType == ResponseTypeSingleton || e.ResponseType == "")
|
||||
}
|
||||
|
||||
// IsChunkedResponse returns true if an export has a chunked response
|
||||
func (e *Export) IsChunkedResponse() bool {
|
||||
return e.Type == Service && e.ResponseType == ResponseTypeChunked
|
||||
}
|
||||
|
||||
// IsStreamResponse returns true if an export has a chunked response
|
||||
func (e *Export) IsStreamResponse() bool {
|
||||
return e.Type == Service && e.ResponseType == ResponseTypeStream
|
||||
}
|
||||
|
||||
// Validate appends validation issues to the passed in results list
|
||||
func (e *Export) Validate(vr *ValidationResults) {
|
||||
if !e.IsService() && !e.IsStream() {
|
||||
vr.AddError("invalid export type: %q", e.Type)
|
||||
}
|
||||
if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() {
|
||||
vr.AddError("invalid response type for service: %q", e.ResponseType)
|
||||
}
|
||||
if e.IsStream() && e.ResponseType != "" {
|
||||
vr.AddError("invalid response type for stream: %q", e.ResponseType)
|
||||
}
|
||||
e.Subject.Validate(vr)
|
||||
}
|
||||
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -1,4 +1,4 @@
|
||||
# github.com/nats-io/jwt v0.2.13-0.20190726194050-829b612a49c3
|
||||
# github.com/nats-io/jwt v0.2.13-0.20190807221443-88776a07123f
|
||||
github.com/nats-io/jwt
|
||||
# github.com/nats-io/nats.go v1.8.1
|
||||
github.com/nats-io/nats.go
|
||||
|
||||
Reference in New Issue
Block a user