summaryrefslogtreecommitdiff
path: root/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle')
-rw-r--r--vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go200
-rw-r--r--vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go43
-rw-r--r--vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go105
-rw-r--r--vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go12
4 files changed, 360 insertions, 0 deletions
diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go
new file mode 100644
index 0000000..ebd3cac
--- /dev/null
+++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/bundle.go
@@ -0,0 +1,200 @@
+package jwtbundle
+
+import (
+ "crypto"
+ "encoding/json"
+ "errors"
+ "io"
+ "os"
+ "sync"
+
+ "github.com/go-jose/go-jose/v4"
+ "github.com/spiffe/go-spiffe/v2/internal/jwtutil"
+ "github.com/spiffe/go-spiffe/v2/spiffeid"
+ "github.com/zeebo/errs"
+)
+
+var jwtbundleErr = errs.Class("jwtbundle")
+
+// Bundle is a collection of trusted JWT authorities for a trust domain.
+type Bundle struct {
+ trustDomain spiffeid.TrustDomain
+
+ mtx sync.RWMutex
+ jwtAuthorities map[string]crypto.PublicKey
+}
+
+// New creates a new bundle.
+func New(trustDomain spiffeid.TrustDomain) *Bundle {
+ return &Bundle{
+ trustDomain: trustDomain,
+ jwtAuthorities: make(map[string]crypto.PublicKey),
+ }
+}
+
+// FromJWTAuthorities creates a new bundle from JWT authorities
+func FromJWTAuthorities(trustDomain spiffeid.TrustDomain, jwtAuthorities map[string]crypto.PublicKey) *Bundle {
+ return &Bundle{
+ trustDomain: trustDomain,
+ jwtAuthorities: jwtutil.CopyJWTAuthorities(jwtAuthorities),
+ }
+}
+
+// Load loads a bundle from a file on disk. The file must contain a standard RFC 7517 JWKS document.
+func Load(trustDomain spiffeid.TrustDomain, path string) (*Bundle, error) {
+ bundleBytes, err := os.ReadFile(path)
+ if err != nil {
+ return nil, jwtbundleErr.New("unable to read JWT bundle: %w", err)
+ }
+
+ return Parse(trustDomain, bundleBytes)
+}
+
+// Read decodes a bundle from a reader. The contents must contain a standard RFC 7517 JWKS document.
+func Read(trustDomain spiffeid.TrustDomain, r io.Reader) (*Bundle, error) {
+ b, err := io.ReadAll(r)
+ if err != nil {
+ return nil, jwtbundleErr.New("unable to read: %v", err)
+ }
+
+ return Parse(trustDomain, b)
+}
+
+// Parse parses a bundle from bytes. The data must be a standard RFC 7517 JWKS document.
+func Parse(trustDomain spiffeid.TrustDomain, bundleBytes []byte) (*Bundle, error) {
+ jwks := new(jose.JSONWebKeySet)
+ if err := json.Unmarshal(bundleBytes, jwks); err != nil {
+ return nil, jwtbundleErr.New("unable to parse JWKS: %v", err)
+ }
+
+ bundle := New(trustDomain)
+ for i, key := range jwks.Keys {
+ if err := bundle.AddJWTAuthority(key.KeyID, key.Key); err != nil {
+ return nil, jwtbundleErr.New("error adding authority %d of JWKS: %v", i, errors.Unwrap(err))
+ }
+ }
+
+ return bundle, nil
+}
+
+// TrustDomain returns the trust domain that the bundle belongs to.
+func (b *Bundle) TrustDomain() spiffeid.TrustDomain {
+ return b.trustDomain
+}
+
+// JWTAuthorities returns the JWT authorities in the bundle, keyed by key ID.
+func (b *Bundle) JWTAuthorities() map[string]crypto.PublicKey {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ return jwtutil.CopyJWTAuthorities(b.jwtAuthorities)
+}
+
+// FindJWTAuthority finds the JWT authority with the given key ID from the bundle. If the authority
+// is found, it is returned and the boolean is true. Otherwise, the returned
+// value is nil and the boolean is false.
+func (b *Bundle) FindJWTAuthority(keyID string) (crypto.PublicKey, bool) {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ if jwtAuthority, ok := b.jwtAuthorities[keyID]; ok {
+ return jwtAuthority, true
+ }
+ return nil, false
+}
+
+// HasJWTAuthority returns true if the bundle has a JWT authority with the given key ID.
+func (b *Bundle) HasJWTAuthority(keyID string) bool {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ _, ok := b.jwtAuthorities[keyID]
+ return ok
+}
+
+// AddJWTAuthority adds a JWT authority to the bundle. If a JWT authority already exists
+// under the given key ID, it is replaced. A key ID must be specified.
+func (b *Bundle) AddJWTAuthority(keyID string, jwtAuthority crypto.PublicKey) error {
+ if keyID == "" {
+ return jwtbundleErr.New("keyID cannot be empty")
+ }
+
+ b.mtx.Lock()
+ defer b.mtx.Unlock()
+
+ b.jwtAuthorities[keyID] = jwtAuthority
+ return nil
+}
+
+// RemoveJWTAuthority removes the JWT authority identified by the key ID from the bundle.
+func (b *Bundle) RemoveJWTAuthority(keyID string) {
+ b.mtx.Lock()
+ defer b.mtx.Unlock()
+
+ delete(b.jwtAuthorities, keyID)
+}
+
+// SetJWTAuthorities sets the JWT authorities in the bundle.
+func (b *Bundle) SetJWTAuthorities(jwtAuthorities map[string]crypto.PublicKey) {
+ b.mtx.Lock()
+ defer b.mtx.Unlock()
+
+ b.jwtAuthorities = jwtutil.CopyJWTAuthorities(jwtAuthorities)
+}
+
+// Empty returns true if the bundle has no JWT authorities.
+func (b *Bundle) Empty() bool {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ return len(b.jwtAuthorities) == 0
+}
+
+// Marshal marshals the JWT bundle into a standard RFC 7517 JWKS document. The
+// JWKS does not contain any SPIFFE-specific parameters.
+func (b *Bundle) Marshal() ([]byte, error) {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ jwks := jose.JSONWebKeySet{}
+ for keyID, jwtAuthority := range b.jwtAuthorities {
+ jwks.Keys = append(jwks.Keys, jose.JSONWebKey{
+ Key: jwtAuthority,
+ KeyID: keyID,
+ })
+ }
+
+ return json.Marshal(jwks)
+}
+
+// Clone clones the bundle.
+func (b *Bundle) Clone() *Bundle {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ return FromJWTAuthorities(b.trustDomain, b.jwtAuthorities)
+}
+
+// Equal compares the bundle for equality against the given bundle.
+func (b *Bundle) Equal(other *Bundle) bool {
+ if b == nil || other == nil {
+ return b == other
+ }
+
+ return b.trustDomain == other.trustDomain &&
+ jwtutil.JWTAuthoritiesEqual(b.jwtAuthorities, other.jwtAuthorities)
+}
+
+// GetJWTBundleForTrustDomain returns the JWT bundle for the given trust
+// domain. It implements the Source interface. An error will be returned if
+// the trust domain does not match that of the bundle.
+func (b *Bundle) GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error) {
+ b.mtx.RLock()
+ defer b.mtx.RUnlock()
+
+ if b.trustDomain != trustDomain {
+ return nil, jwtbundleErr.New("no JWT bundle for trust domain %q", trustDomain)
+ }
+
+ return b, nil
+}
diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go
new file mode 100644
index 0000000..394878e
--- /dev/null
+++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/doc.go
@@ -0,0 +1,43 @@
+// Package jwtbundle provides JWT bundle related functionality.
+//
+// A bundle represents a collection of JWT authorities, i.e., those that
+// are used to authenticate SPIFFE JWT-SVIDs.
+//
+// You can create a new bundle for a specific trust domain:
+//
+// td := spiffeid.RequireTrustDomainFromString("example.org")
+// bundle := jwtbundle.New(td)
+//
+// Or you can load it from disk:
+//
+// td := spiffeid.RequireTrustDomainFromString("example.org")
+// bundle := jwtbundle.Load(td, "bundle.jwks")
+//
+// The bundle can be initialized with JWT authorities:
+//
+// td := spiffeid.RequireTrustDomainFromString("example.org")
+// var jwtAuthorities map[string]crypto.PublicKey = ...
+// bundle := jwtbundle.FromJWTAuthorities(td, jwtAuthorities)
+//
+// In addition, you can add JWT authorities to the bundle:
+//
+// var keyID string = ...
+// var publicKey crypto.PublicKey = ...
+// bundle.AddJWTAuthority(keyID, publicKey)
+//
+// Bundles can be organized into a set, keyed by trust domain:
+//
+// set := jwtbundle.NewSet()
+// set.Add(bundle)
+//
+// A Source is source of JWT bundles for a trust domain. Both the Bundle
+// and Set types implement Source:
+//
+// // Initialize the source from a bundle or set
+// var source jwtbundle.Source = bundle
+// // ... or ...
+// var source jwtbundle.Source = set
+//
+// // Use the source to query for bundles by trust domain
+// bundle, err := source.GetJWTBundleForTrustDomain(td)
+package jwtbundle
diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go
new file mode 100644
index 0000000..048dd0d
--- /dev/null
+++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/set.go
@@ -0,0 +1,105 @@
+package jwtbundle
+
+import (
+ "sort"
+ "sync"
+
+ "github.com/spiffe/go-spiffe/v2/spiffeid"
+)
+
+// Set is a set of bundles, keyed by trust domain.
+type Set struct {
+ mtx sync.RWMutex
+ bundles map[spiffeid.TrustDomain]*Bundle
+}
+
+// NewSet creates a new set initialized with the given bundles.
+func NewSet(bundles ...*Bundle) *Set {
+ bundlesMap := make(map[spiffeid.TrustDomain]*Bundle)
+
+ for _, b := range bundles {
+ if b != nil {
+ bundlesMap[b.trustDomain] = b
+ }
+ }
+
+ return &Set{
+ bundles: bundlesMap,
+ }
+}
+
+// Add adds a new bundle into the set. If a bundle already exists for the
+// trust domain, the existing bundle is replaced.
+func (s *Set) Add(bundle *Bundle) {
+ s.mtx.Lock()
+ defer s.mtx.Unlock()
+
+ if bundle != nil {
+ s.bundles[bundle.trustDomain] = bundle
+ }
+}
+
+// Remove removes the bundle for the given trust domain.
+func (s *Set) Remove(trustDomain spiffeid.TrustDomain) {
+ s.mtx.Lock()
+ defer s.mtx.Unlock()
+
+ delete(s.bundles, trustDomain)
+}
+
+// Has returns true if there is a bundle for the given trust domain.
+func (s *Set) Has(trustDomain spiffeid.TrustDomain) bool {
+ s.mtx.RLock()
+ defer s.mtx.RUnlock()
+
+ _, ok := s.bundles[trustDomain]
+ return ok
+}
+
+// Get returns a bundle for the given trust domain. If the bundle is in the set
+// it is returned and the boolean is true. Otherwise, the returned value is
+// nil and the boolean is false.
+func (s *Set) Get(trustDomain spiffeid.TrustDomain) (*Bundle, bool) {
+ s.mtx.RLock()
+ defer s.mtx.RUnlock()
+
+ bundle, ok := s.bundles[trustDomain]
+ return bundle, ok
+}
+
+// Bundles returns the bundles in the set sorted by trust domain.
+func (s *Set) Bundles() []*Bundle {
+ s.mtx.RLock()
+ defer s.mtx.RUnlock()
+
+ out := make([]*Bundle, 0, len(s.bundles))
+ for _, bundle := range s.bundles {
+ out = append(out, bundle)
+ }
+ sort.Slice(out, func(a, b int) bool {
+ return out[a].TrustDomain().Compare(out[b].TrustDomain()) < 0
+ })
+ return out
+}
+
+// Len returns the number of bundles in the set.
+func (s *Set) Len() int {
+ s.mtx.RLock()
+ defer s.mtx.RUnlock()
+
+ return len(s.bundles)
+}
+
+// GetJWTBundleForTrustDomain returns the JWT bundle for the given trust
+// domain. It implements the Source interface.
+func (s *Set) GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error) {
+ s.mtx.RLock()
+ defer s.mtx.RUnlock()
+
+ bundle, ok := s.bundles[trustDomain]
+ if !ok {
+ return nil, jwtbundleErr.New("no JWT bundle for trust domain %q", trustDomain)
+ }
+
+ return bundle, nil
+}
diff --git a/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go
new file mode 100644
index 0000000..224cd9f
--- /dev/null
+++ b/vendor/github.com/spiffe/go-spiffe/v2/bundle/jwtbundle/source.go
@@ -0,0 +1,12 @@
+package jwtbundle
+
+import (
+ "github.com/spiffe/go-spiffe/v2/spiffeid"
+)
+
+// Source represents a source of JWT bundles keyed by trust domain.
+type Source interface {
+ // GetJWTBundleForTrustDomain returns the JWT bundle for the given trust
+ // domain.
+ GetJWTBundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*Bundle, error)
+}