feat: add end 2 end tests

This commit is contained in:
Jon Lundy
2023-01-25 10:35:09 -07:00
parent f9a088269c
commit 0f504a98e9
13 changed files with 268 additions and 230 deletions

46
app/webfinger/client.go Normal file
View 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)
}

View File

@@ -32,6 +32,19 @@ type JRD struct {
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)
// Link is a link to a related resource.

View File

@@ -20,8 +20,9 @@ import (
)
type service struct {
es *ev.EventStore
self set.Set[string]
es *ev.EventStore
self set.Set[string]
cache func(string) bool
}
type Option interface {
@@ -34,6 +35,12 @@ func (o WithHostnames) ApplyWebfinger(s *service) {
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) {
ctx, span := lg.Span(ctx)
defer span.End()
@@ -91,10 +98,9 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.Body.Close()
type claims struct {
Subject string `json:"sub"`
PubKey string `json:"pub"`
PubKey string `json:"pub"`
*JRD
jwt.StandardClaims
jwt.RegisteredClaims
}
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")
}
c.JRD.Subject = c.Subject
c.StandardClaims.Subject = c.Subject
c.JRD.Subject = c.RegisteredClaims.Subject
c.SetProperty(NSpubkey, &c.PubKey)
@@ -134,7 +139,17 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
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
// 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")
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.Scheme = "https"
redirect.Scheme = u.URL.Scheme
redirect.Host = u.URL.Host
redirect.RawQuery = r.URL.RawQuery
redirect.Path = "/.well-known/webfinger"
@@ -279,3 +301,31 @@ func dec(s string) ([]byte, error) {
s = strings.TrimSpace(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
}