feat: add end 2 end tests
This commit is contained in:
		
							parent
							
								
									f9a088269c
								
							
						
					
					
						commit
						0f504a98e9
					
				
							
								
								
									
										46
									
								
								app/webfinger/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/webfinger/client.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package webfinger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/ed25519"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/golang-jwt/jwt/v4"
 | 
				
			||||||
 | 
						"github.com/oklog/ulid/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultExpire   = 30 * time.Minute
 | 
				
			||||||
 | 
						defaultIssuer   = "sour.is/webfinger"
 | 
				
			||||||
 | 
						defaultAudience = "sour.is/webfinger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSignedRequest(jrd *JRD, key ed25519.PrivateKey) (string, error) {
 | 
				
			||||||
 | 
						type claims struct {
 | 
				
			||||||
 | 
							PubKey string `json:"pub"`
 | 
				
			||||||
 | 
							*JRD
 | 
				
			||||||
 | 
							jwt.RegisteredClaims
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pub := []byte(key.Public().(ed25519.PublicKey))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						j := claims{
 | 
				
			||||||
 | 
							PubKey: enc(pub),
 | 
				
			||||||
 | 
							JRD:    jrd.CloneValues(),
 | 
				
			||||||
 | 
							RegisteredClaims: jwt.RegisteredClaims{
 | 
				
			||||||
 | 
								ID:        ulid.Make().String(),
 | 
				
			||||||
 | 
								Subject:   jrd.Subject,
 | 
				
			||||||
 | 
								Audience:  jwt.ClaimStrings{defaultAudience},
 | 
				
			||||||
 | 
								ExpiresAt: jwt.NewNumericDate(time.Now().Add(defaultExpire)),
 | 
				
			||||||
 | 
								IssuedAt:  jwt.NewNumericDate(time.Now()),
 | 
				
			||||||
 | 
								Issuer:    defaultIssuer,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						j.JRD.Subject = "" // move subject into registered claims.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, &j)
 | 
				
			||||||
 | 
						return token.SignedString(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func enc(b []byte) string {
 | 
				
			||||||
 | 
						return base64.RawURLEncoding.EncodeToString(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -32,6 +32,19 @@ type JRD struct {
 | 
				
			|||||||
	event.AggregateRoot `yaml:"-"`
 | 
						event.AggregateRoot `yaml:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *JRD) CloneValues() *JRD {
 | 
				
			||||||
 | 
						m := make(map[string]*string, len(a.Properties))
 | 
				
			||||||
 | 
						for k,v := range a.Properties {
 | 
				
			||||||
 | 
							m[k] = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &JRD{
 | 
				
			||||||
 | 
							Subject:       a.Subject,
 | 
				
			||||||
 | 
							Aliases:       append([]string{}, a.Aliases...),
 | 
				
			||||||
 | 
							Properties:    m,
 | 
				
			||||||
 | 
							Links:         append([]*Link{}, a.Links...),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ event.Aggregate = (*JRD)(nil)
 | 
					var _ event.Aggregate = (*JRD)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Link is a link to a related resource.
 | 
					// Link is a link to a related resource.
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type service struct {
 | 
					type service struct {
 | 
				
			||||||
	es   *ev.EventStore
 | 
						es    *ev.EventStore
 | 
				
			||||||
	self set.Set[string]
 | 
						self  set.Set[string]
 | 
				
			||||||
 | 
						cache func(string) bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Option interface {
 | 
					type Option interface {
 | 
				
			||||||
@ -34,6 +35,12 @@ func (o WithHostnames) ApplyWebfinger(s *service) {
 | 
				
			|||||||
	s.self = set.New(o...)
 | 
						s.self = set.New(o...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WithCache func(string) bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o WithCache) ApplyWebfinger(s *service) {
 | 
				
			||||||
 | 
						s.cache = o
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(ctx context.Context, es *ev.EventStore, opts ...Option) (*service, error) {
 | 
					func New(ctx context.Context, es *ev.EventStore, opts ...Option) (*service, error) {
 | 
				
			||||||
	ctx, span := lg.Span(ctx)
 | 
						ctx, span := lg.Span(ctx)
 | 
				
			||||||
	defer span.End()
 | 
						defer span.End()
 | 
				
			||||||
@ -91,10 +98,9 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
		r.Body.Close()
 | 
							r.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		type claims struct {
 | 
							type claims struct {
 | 
				
			||||||
			Subject string `json:"sub"`
 | 
								PubKey string `json:"pub"`
 | 
				
			||||||
			PubKey  string `json:"pub"`
 | 
					 | 
				
			||||||
			*JRD
 | 
								*JRD
 | 
				
			||||||
			jwt.StandardClaims
 | 
								jwt.RegisteredClaims
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		token, err := jwt.ParseWithClaims(
 | 
							token, err := jwt.ParseWithClaims(
 | 
				
			||||||
@ -106,8 +112,7 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
					return nil, fmt.Errorf("wrong type of claim")
 | 
										return nil, fmt.Errorf("wrong type of claim")
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				c.JRD.Subject = c.Subject
 | 
									c.JRD.Subject = c.RegisteredClaims.Subject
 | 
				
			||||||
				c.StandardClaims.Subject = c.Subject
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				c.SetProperty(NSpubkey, &c.PubKey)
 | 
									c.SetProperty(NSpubkey, &c.PubKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -134,7 +139,17 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		a, err := ev.Upsert(ctx, s.es, StreamID(c.Subject), func(ctx context.Context, a *JRD) error {
 | 
							if c.ID != "" && s.cache != nil {
 | 
				
			||||||
 | 
								if ok := s.cache(c.ID); ok {
 | 
				
			||||||
 | 
									w.WriteHeader(http.StatusAlreadyReported)
 | 
				
			||||||
 | 
									fmt.Fprint(w, http.StatusText(http.StatusAlreadyReported))
 | 
				
			||||||
 | 
									span.AddEvent("already seen ID")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							a, err := ev.Upsert(ctx, s.es, StreamID(c.JRD.Subject), func(ctx context.Context, a *JRD) error {
 | 
				
			||||||
			var auth *JRD
 | 
								var auth *JRD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// does the target have a pubkey for self auth?
 | 
								// does the target have a pubkey for self auth?
 | 
				
			||||||
@ -211,9 +226,16 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
				
			|||||||
		resource := r.URL.Query().Get("resource")
 | 
							resource := r.URL.Query().Get("resource")
 | 
				
			||||||
		rels := r.URL.Query()["rel"]
 | 
							rels := r.URL.Query()["rel"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if u := Parse(resource); u != nil && !s.self.Has(u.URL.Hostname()) {
 | 
							if resource == "" {
 | 
				
			||||||
 | 
								w.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							host, _ := splitHostPort(r.Host)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if u := Parse(resource); u != nil && !s.self.Has(host) {
 | 
				
			||||||
			redirect := &url.URL{}
 | 
								redirect := &url.URL{}
 | 
				
			||||||
			redirect.Scheme = "https"
 | 
								redirect.Scheme = u.URL.Scheme
 | 
				
			||||||
			redirect.Host = u.URL.Host
 | 
								redirect.Host = u.URL.Host
 | 
				
			||||||
			redirect.RawQuery = r.URL.RawQuery
 | 
								redirect.RawQuery = r.URL.RawQuery
 | 
				
			||||||
			redirect.Path = "/.well-known/webfinger"
 | 
								redirect.Path = "/.well-known/webfinger"
 | 
				
			||||||
@ -279,3 +301,31 @@ func dec(s string) ([]byte, error) {
 | 
				
			|||||||
	s = strings.TrimSpace(s)
 | 
						s = strings.TrimSpace(s)
 | 
				
			||||||
	return base64.RawURLEncoding.DecodeString(s)
 | 
						return base64.RawURLEncoding.DecodeString(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					func splitHostPort(hostPort string) (host, port string) {
 | 
				
			||||||
 | 
						host = hostPort
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						colon := strings.LastIndexByte(host, ':')
 | 
				
			||||||
 | 
						if colon != -1 && validOptionalPort(host[colon:]) {
 | 
				
			||||||
 | 
							host, port = host[:colon], host[colon+1:]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
 | 
				
			||||||
 | 
							host = host[1 : len(host)-1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func validOptionalPort(port string) bool {
 | 
				
			||||||
 | 
						if port == "" {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if port[0] != ':' {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, b := range port[1:] {
 | 
				
			||||||
 | 
							if b < '0' || b > '9' {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -4,7 +4,9 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/patrickmn/go-cache"
 | 
				
			||||||
	"github.com/sour-is/ev"
 | 
						"github.com/sour-is/ev"
 | 
				
			||||||
	"github.com/sour-is/ev/app/webfinger"
 | 
						"github.com/sour-is/ev/app/webfinger"
 | 
				
			||||||
	"github.com/sour-is/ev/internal/lg"
 | 
						"github.com/sour-is/ev/internal/lg"
 | 
				
			||||||
@ -13,6 +15,11 @@ import (
 | 
				
			|||||||
	"github.com/sour-is/ev/pkg/slice"
 | 
						"github.com/sour-is/ev/pkg/slice"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						defaultExpire   = 3 * time.Minute
 | 
				
			||||||
 | 
						cleanupInterval = 10 * time.Minute
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error {
 | 
					var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error {
 | 
				
			||||||
	ctx, span := lg.Span(ctx)
 | 
						ctx, span := lg.Span(ctx)
 | 
				
			||||||
	defer span.End()
 | 
						defer span.End()
 | 
				
			||||||
@ -23,12 +30,17 @@ var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error
 | 
				
			|||||||
		return fmt.Errorf("*es.EventStore not found in services")
 | 
							return fmt.Errorf("*es.EventStore not found in services")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wf, err := webfinger.New(
 | 
						cache := cache.New(defaultExpire, cleanupInterval)
 | 
				
			||||||
		ctx, 
 | 
						var withCache webfinger.WithCache = (func(s string) bool {
 | 
				
			||||||
		eventstore, 
 | 
							if _, ok := cache.Get(s); ok {
 | 
				
			||||||
		webfinger.WithHostnames(
 | 
								return true
 | 
				
			||||||
			strings.Fields(env.Default("WEBFINGER_DOMAINS", "sour.is")),
 | 
							}
 | 
				
			||||||
		))
 | 
							cache.SetDefault(s, true)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						var withHostnames webfinger.WithHostnames = strings.Fields(env.Default("WEBFINGER_DOMAINS", "sour.is"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wf, err := webfinger.New(ctx, eventstore, withCache, withHostnames)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		span.RecordError(err)
 | 
							span.RecordError(err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
 | 
				
			|||||||
@ -21,17 +21,21 @@ func main() {
 | 
				
			|||||||
		<-ctx.Done()
 | 
							<-ctx.Done()
 | 
				
			||||||
		defer cancel() // restore interrupt function
 | 
							defer cancel() // restore interrupt function
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
						if err := Run(ctx); err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func Run(ctx context.Context) error {
 | 
				
			||||||
	svc := &service.Harness{}
 | 
						svc := &service.Harness{}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx, stop := lg.Init(ctx, appName)
 | 
						ctx, stop := lg.Init(ctx, appName)
 | 
				
			||||||
	svc.OnStop(stop)
 | 
						svc.OnStop(stop)
 | 
				
			||||||
	svc.Add(lg.NewHTTP(ctx))
 | 
						svc.Add(lg.NewHTTP(ctx))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	svc.Setup(ctx, apps.Apps()...)
 | 
						svc.Setup(ctx, apps.Apps()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Run application
 | 
						// Run application
 | 
				
			||||||
	if err := svc.Run(ctx, appName, version); err != nil && !errors.Is(err, http.ErrServerClosed) {
 | 
						if err := svc.Run(ctx, appName, version); err != nil && !errors.Is(err, http.ErrServerClosed) {
 | 
				
			||||||
		log.Fatal(err)
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,10 +14,8 @@ import (
 | 
				
			|||||||
	"os/signal"
 | 
						"os/signal"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/docopt/docopt-go"
 | 
						"github.com/docopt/docopt-go"
 | 
				
			||||||
	"github.com/golang-jwt/jwt"
 | 
					 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/sour-is/ev/app/webfinger"
 | 
						"github.com/sour-is/ev/app/webfinger"
 | 
				
			||||||
@ -127,23 +125,14 @@ func run(opts opts) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bkey := []byte(key.Public().(ed25519.PublicKey))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{
 | 
							jrd := &webfinger.JRD{Subject: opts.Subject}
 | 
				
			||||||
			"sub":     opts.Subject,
 | 
							token, err := webfinger.NewSignedRequest(jrd, key)
 | 
				
			||||||
			"subject": opts.Subject,
 | 
					 | 
				
			||||||
			"pub":     enc(bkey),
 | 
					 | 
				
			||||||
			"exp":     time.Now().Add(90 * time.Minute).Unix(),
 | 
					 | 
				
			||||||
			"iat":     time.Now().Unix(),
 | 
					 | 
				
			||||||
			"aud":     "webfinger",
 | 
					 | 
				
			||||||
			"iss":     "sour.is-webfingerCLI",
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		aToken, err := token.SignedString(key)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		body := strings.NewReader(aToken)
 | 
							body := strings.NewReader(token)
 | 
				
			||||||
		req, err := http.NewRequest(http.MethodDelete, url.String(), body)
 | 
							req, err := http.NewRequest(http.MethodDelete, url.String(), body)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
@ -173,7 +162,6 @@ func run(opts opts) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bkey := []byte(key.Public().(ed25519.PublicKey))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmt.Fprintln(os.Stderr, opts.File)
 | 
							fmt.Fprintln(os.Stderr, opts.File)
 | 
				
			||||||
		fp, err := os.Open(opts.File)
 | 
							fp, err := os.Open(opts.File)
 | 
				
			||||||
@ -182,40 +170,20 @@ func run(opts opts) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		y := yaml.NewDecoder(fp)
 | 
							y := yaml.NewDecoder(fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		type claims struct {
 | 
					 | 
				
			||||||
			Subject string `json:"sub"`
 | 
					 | 
				
			||||||
			PubKey  string `json:"pub"`
 | 
					 | 
				
			||||||
			*webfinger.JRD
 | 
					 | 
				
			||||||
			jwt.StandardClaims
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for err == nil {
 | 
							for err == nil {
 | 
				
			||||||
			j := claims{
 | 
								jrd := &webfinger.JRD{}
 | 
				
			||||||
				PubKey: enc(bkey),
 | 
					 | 
				
			||||||
				JRD:    &webfinger.JRD{},
 | 
					 | 
				
			||||||
				StandardClaims: jwt.StandardClaims{
 | 
					 | 
				
			||||||
					Audience:  "sour.is-webfinger",
 | 
					 | 
				
			||||||
					ExpiresAt: time.Now().Add(30 * time.Minute).Unix(),
 | 
					 | 
				
			||||||
					IssuedAt:  time.Now().Unix(),
 | 
					 | 
				
			||||||
					Issuer:    "sour.is-webfingerCLI",
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = y.Decode(j.JRD)
 | 
								err = y.Decode(jrd)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			j.Subject = j.JRD.Subject
 | 
								token, err := webfinger.NewSignedRequest(jrd, key)
 | 
				
			||||||
			j.StandardClaims.Subject = j.JRD.Subject
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, &j)
 | 
					 | 
				
			||||||
			aToken, err := token.SignedString(key)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			body := strings.NewReader(aToken)
 | 
								body := strings.NewReader(token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req, err := http.NewRequest(http.MethodPut, url.String(), body)
 | 
								req, err := http.NewRequest(http.MethodPut, url.String(), body)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/app/webfinger"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/internal/lg"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/service"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/slice"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error {
 | 
					 | 
				
			||||||
	ctx, span := lg.Span(ctx)
 | 
					 | 
				
			||||||
	defer span.End()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	span.AddEvent("Enable WebFinger")
 | 
					 | 
				
			||||||
	eventstore, ok := slice.Find[*ev.EventStore](svc.Services...)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return fmt.Errorf("*es.EventStore not found in services")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wf, err := webfinger.New(ctx, eventstore)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		span.RecordError(err)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	svc.Add(wf)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								cmd/webfinger/app.webfinger.go
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								cmd/webfinger/app.webfinger.go
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					../ev/app.webfinger.go
 | 
				
			||||||
@ -1,37 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"os/signal"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/internal/lg"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/service"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var apps service.Apps
 | 
					 | 
				
			||||||
var appName, version = service.AppName()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		<-ctx.Done()
 | 
					 | 
				
			||||||
		defer cancel() // restore interrupt function
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	svc := &service.Harness{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx, stop := lg.Init(ctx, appName)
 | 
					 | 
				
			||||||
	svc.OnStop(stop)
 | 
					 | 
				
			||||||
	svc.Add(lg.NewHTTP(ctx))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	svc.Setup(ctx, apps.Apps()...)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Run application
 | 
					 | 
				
			||||||
	if err := svc.Run(ctx, appName, version); err != nil && !errors.Is(err, http.ErrServerClosed) {
 | 
					 | 
				
			||||||
		log.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								cmd/webfinger/main.go
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								cmd/webfinger/main.go
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					../ev/main.go
 | 
				
			||||||
@ -1,53 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/internal/lg"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/env"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/es"
 | 
					 | 
				
			||||||
	diskstore "github.com/sour-is/ev/pkg/es/driver/disk-store"
 | 
					 | 
				
			||||||
	memstore "github.com/sour-is/ev/pkg/es/driver/mem-store"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/es/driver/projecter"
 | 
					 | 
				
			||||||
	resolvelinks "github.com/sour-is/ev/pkg/es/driver/resolve-links"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/es/driver/streamer"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/es/event"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/service"
 | 
					 | 
				
			||||||
	"go.uber.org/multierr"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ = apps.Register(10, func(ctx context.Context, svc *service.Harness) error {
 | 
					 | 
				
			||||||
	ctx, span := lg.Span(ctx)
 | 
					 | 
				
			||||||
	defer span.End()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// setup eventstore
 | 
					 | 
				
			||||||
	err := multierr.Combine(
 | 
					 | 
				
			||||||
		ev.Init(ctx),
 | 
					 | 
				
			||||||
		event.Init(ctx),
 | 
					 | 
				
			||||||
		diskstore.Init(ctx),
 | 
					 | 
				
			||||||
		memstore.Init(ctx),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		span.RecordError(err)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	eventstore, err := ev.Open(
 | 
					 | 
				
			||||||
		ctx,
 | 
					 | 
				
			||||||
		env.Default("EV_DATA", "mem:"),
 | 
					 | 
				
			||||||
		resolvelinks.New(),
 | 
					 | 
				
			||||||
		streamer.New(ctx),
 | 
					 | 
				
			||||||
		projecter.New(
 | 
					 | 
				
			||||||
			ctx,
 | 
					 | 
				
			||||||
			projecter.DefaultProjection,
 | 
					 | 
				
			||||||
		),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		span.RecordError(err)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	svc.Add(eventstore, &es.EventStore{EventStore: eventstore})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								cmd/webfinger/svc.es.go
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								cmd/webfinger/svc.es.go
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					../ev/svc.es.go
 | 
				
			||||||
@ -1,46 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/rs/cors"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/internal/lg"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/env"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/mux"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/service"
 | 
					 | 
				
			||||||
	"github.com/sour-is/ev/pkg/slice"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ = apps.Register(20, func(ctx context.Context, svc *service.Harness) error {
 | 
					 | 
				
			||||||
	s := &http.Server{}
 | 
					 | 
				
			||||||
	svc.Add(s)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mux := mux.New()
 | 
					 | 
				
			||||||
	s.Handler = cors.AllowAll().Handler(mux)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
					 | 
				
			||||||
	// 	log.Println(r.URL.Path)
 | 
					 | 
				
			||||||
	// 	mux.ServeHTTP(w, r)
 | 
					 | 
				
			||||||
	// })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.Addr = env.Default("EV_HTTP", ":8080")
 | 
					 | 
				
			||||||
	if strings.HasPrefix(s.Addr, ":") {
 | 
					 | 
				
			||||||
		s.Addr = "[::]" + s.Addr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	svc.OnStart(func(ctx context.Context) error {
 | 
					 | 
				
			||||||
		_, span := lg.Span(ctx)
 | 
					 | 
				
			||||||
		defer span.End()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		log.Print("Listen on ", s.Addr)
 | 
					 | 
				
			||||||
		span.AddEvent("begin listen and serve on " + s.Addr)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mux.Add(slice.FilterType[interface{ RegisterHTTP(*http.ServeMux) }](svc.Services...)...)
 | 
					 | 
				
			||||||
		return s.ListenAndServe()
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	svc.OnStop(s.Shutdown)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								cmd/webfinger/svc.http.go
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								cmd/webfinger/svc.http.go
									
									
									
									
									
										Symbolic link
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					../ev/svc.http.go
 | 
				
			||||||
							
								
								
									
										108
									
								
								cmd/webfinger/webfinger_e2e_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								cmd/webfinger/webfinger_e2e_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/ed25519"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/matryer/is"
 | 
				
			||||||
 | 
						"github.com/sour-is/ev/app/webfinger"
 | 
				
			||||||
 | 
						"golang.org/x/sync/errgroup"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
 | 
						data, err := os.MkdirTemp("", "data*")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("error creating data dir: %s\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os.Setenv("EV_HTTP", "[::1]:61234")
 | 
				
			||||||
 | 
						os.Setenv("WEBFINGER_DOMAINS", "::1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wg, ctx := errgroup.WithContext(ctx)
 | 
				
			||||||
 | 
						wg.Go(func() error {
 | 
				
			||||||
 | 
							// Run application
 | 
				
			||||||
 | 
							if err := Run(ctx); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						wg.Go(func() error {
 | 
				
			||||||
 | 
							m.Run()
 | 
				
			||||||
 | 
							cancel()
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := wg.Wait(); err != nil {
 | 
				
			||||||
 | 
							fmt.Println(err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetHTTP(t *testing.T) {
 | 
				
			||||||
 | 
						is := is.New(t)
 | 
				
			||||||
 | 
						res, err := http.DefaultClient.Get("http://[::1]:61234/.well-known/webfinger")
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
						is.Equal(res.StatusCode, http.StatusBadRequest)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateResource(t *testing.T) {
 | 
				
			||||||
 | 
						is := is.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, priv, err := ed25519.GenerateKey(nil)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jrd := &webfinger.JRD{
 | 
				
			||||||
 | 
							Subject: "me@sour.is",
 | 
				
			||||||
 | 
							Properties: map[string]*string{
 | 
				
			||||||
 | 
								"foo": ptr("bar"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create
 | 
				
			||||||
 | 
						token, err := webfinger.NewSignedRequest(jrd, priv)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, err := http.NewRequest(http.MethodPut, "http://[::1]:61234/.well-known/webfinger", strings.NewReader(token))
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := http.DefaultClient.Do(req)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						is.Equal(res.StatusCode, http.StatusCreated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// repeat
 | 
				
			||||||
 | 
						req, err = http.NewRequest(http.MethodPut, "http://[::1]:61234/.well-known/webfinger", strings.NewReader(token))
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err = http.DefaultClient.Do(req)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						is.Equal(res.StatusCode, http.StatusAlreadyReported)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// fetch
 | 
				
			||||||
 | 
						req, err = http.NewRequest(http.MethodGet, "http://[::1]:61234/.well-known/webfinger?resource=me@sour.is", nil)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err = http.DefaultClient.Do(req)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						is.Equal(res.StatusCode, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resJRD := &webfinger.JRD{}
 | 
				
			||||||
 | 
						err = json.NewDecoder(res.Body).Decode(resJRD)
 | 
				
			||||||
 | 
						is.NoErr(err)
 | 
				
			||||||
 | 
						is.Equal(jrd.Subject, resJRD.Subject)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptr[T any](t T) *T { return &t }
 | 
				
			||||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							@ -24,6 +24,8 @@ require (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require github.com/tj/go-semver v1.0.0
 | 
					require github.com/tj/go-semver v1.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	git.mills.io/prologic/msgbus v0.1.19 // indirect
 | 
						git.mills.io/prologic/msgbus v0.1.19 // indirect
 | 
				
			||||||
	github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 // indirect
 | 
						github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5 // indirect
 | 
				
			||||||
@ -101,7 +103,6 @@ require (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
 | 
						github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
 | 
				
			||||||
	github.com/golang-jwt/jwt v3.2.2+incompatible
 | 
					 | 
				
			||||||
	github.com/golang-jwt/jwt/v4 v4.4.3
 | 
						github.com/golang-jwt/jwt/v4 v4.4.3
 | 
				
			||||||
	github.com/keys-pub/keys v0.1.22
 | 
						github.com/keys-pub/keys v0.1.22
 | 
				
			||||||
	github.com/matryer/is v1.4.0
 | 
						github.com/matryer/is v1.4.0
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							@ -155,8 +155,6 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
 | 
				
			|||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 | 
					github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 | 
				
			||||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 | 
					github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 | 
				
			||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
					github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
				
			||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 | 
					 | 
				
			||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 | 
					 | 
				
			||||||
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
 | 
					github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
 | 
				
			||||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 | 
					github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 | 
				
			||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
					github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
				
			||||||
@ -328,6 +326,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 | 
				
			|||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
					github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
				
			||||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 | 
					github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 | 
				
			||||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 | 
					github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 | 
				
			||||||
 | 
					github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 | 
				
			||||||
 | 
					github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 | 
				
			||||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
 | 
					github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
 | 
				
			||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
 | 
					github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
 | 
				
			||||||
github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a h1:VXRRto5GMJPNfB7MNbUVoFhtxwoYjBEsIt/NpWg42U0=
 | 
					github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a h1:VXRRto5GMJPNfB7MNbUVoFhtxwoYjBEsIt/NpWg42U0=
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user