diff --git a/Makefile b/Makefile index 4b6640d..d865eeb 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,3 @@ ifeq (, $(shell which gqlgen)) endif gqlgen - -EV_HOST?=localhost:8080 -load: - watch -n .1 "http POST $(EV_HOST)/inbox/asdf/test a=b one=1 two:='{\"v\":2}' | jq" diff --git a/app/webfinger/events.go b/app/webfinger/events.go index cb9be8d..3518d91 100644 --- a/app/webfinger/events.go +++ b/app/webfinger/events.go @@ -9,7 +9,7 @@ type SubjectSet struct { Aliases []string `json:"aliases,omitempty"` Properties map[string]*string `json:"properties,omitempty"` - event.IsEvent + event.IsEvent `json:"-"` } var _ event.Event = (*SubjectSet)(nil) @@ -17,27 +17,29 @@ var _ event.Event = (*SubjectSet)(nil) type SubjectDeleted struct { Subject string `json:"subject"` - event.IsEvent + event.IsEvent `json:"-"` } var _ event.Event = (*SubjectDeleted)(nil) type LinkSet struct { + Index uint64 `json:"idx"` Rel string `json:"rel"` Type string `json:"type,omitempty"` HRef string `json:"href,omitempty"` Titles map[string]string `json:"titles,omitempty"` Properties map[string]*string `json:"properties,omitempty"` - event.IsEvent + event.IsEvent `json:"-"` } var _ event.Event = (*LinkSet)(nil) type LinkDeleted struct { - Rel string `json:"rel"` + Index uint64 `json:"idx"` + Rel string `json:"rel"` - event.IsEvent + event.IsEvent `json:"-"` } var _ event.Event = (*LinkDeleted)(nil) diff --git a/app/webfinger/jrd.go b/app/webfinger/jrd.go index 7e6a5bf..40d6875 100644 --- a/app/webfinger/jrd.go +++ b/app/webfinger/jrd.go @@ -23,13 +23,12 @@ func StreamID(subject string) string { // JRD is a JSON Resource Descriptor, specifying properties and related links // for a resource. type JRD struct { - Subject string `json:"subject,omitempty" yaml:"subject,omitempty"` - Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"` - Properties map[string]*string `json:"properties,omitempty" yaml:"properties,omitempty"` - Links Links `json:"links,omitempty" yaml:"links,omitempty"` - + Subject string `json:"subject,omitempty" yaml:"subject,omitempty"` + Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"` + Properties map[string]*string `json:"properties,omitempty" yaml:"properties,omitempty"` + Links Links `json:"links,omitempty" yaml:"links,omitempty"` deleted bool - event.IsAggregate `yaml:"-"` + event.IsAggregate `json:"-" yaml:"-"` } func (a *JRD) CloneValues() *JRD { @@ -49,6 +48,7 @@ var _ event.Aggregate = (*JRD)(nil) // Link is a link to a related resource. type Link struct { + Index uint64 `json:"-" yaml:"-"` Rel string `json:"rel,omitempty"` Type string `json:"type,omitempty"` HRef string `json:"href,omitempty"` @@ -71,6 +71,9 @@ func (l Links) Less(i int, j int) bool { if l[i] == nil || l[j] == nil { return false } + if l[i].Rel == l[j].Rel { + return l[i].Type < l[j].Type + } return l[i].Rel < l[j].Rel } @@ -89,6 +92,9 @@ func ParseJRD(blob []byte) (*JRD, error) { if err != nil { return nil, err } + for i := range jrd.Links { + jrd.Links[i].Index = uint64(i) + } return &jrd, nil } @@ -102,7 +108,7 @@ func (jrd *JRD) GetLinkByRel(rel string) *Link { return nil } -// GetLinksByRel returns the first *Link with the specified rel value. +// GetLinksByRel returns each *Link with the specified rel value. func (jrd *JRD) GetLinksByRel(rel ...string) []*Link { var lis []*Link rels := set.New(rel...) @@ -180,20 +186,21 @@ func (a *JRD) ApplyEvent(events ...event.Event) { a.Properties = map[string]*string{} case *LinkSet: - link, ok := slice.FindFn(func(l *Link) bool { return l.Rel == e.Rel }, a.Links...) + link, ok := slice.FindFn(func(l *Link) bool { return l.Index == e.Index }, a.Links...) if !ok { link = &Link{} - link.Rel = e.Rel + link.Index = uint64(len(a.Links)) a.Links = append(a.Links, link) } + link.Rel = e.Rel link.HRef = e.HRef link.Type = e.Type link.Titles = e.Titles link.Properties = e.Properties case *LinkDeleted: - a.Links = slice.FilterFn(func(link *Link) bool { return link.Rel != e.Rel }, a.Links...) + a.Links = slice.FilterFn(func(link *Link) bool { return link.Index != e.Index }, a.Links...) } } } @@ -241,17 +248,15 @@ func (a *JRD) OnClaims(jrd *JRD) error { return err } - sort.Sort(jrd.Links) - sort.Sort(a.Links) for _, z := range slice.Align( jrd.Links, a.Links, - func(l, r *Link) bool { return l.Rel < r.Rel }, + func(l, r *Link) bool { return l.Index < r.Index }, ) { // Not in new == delete if z.Key == nil { link := *z.Value - event.Raise(a, &LinkDeleted{Rel: link.Rel}) + event.Raise(a, &LinkDeleted{Index: link.Index, Rel: link.Rel}) continue } @@ -259,6 +264,7 @@ func (a *JRD) OnClaims(jrd *JRD) error { if z.Value == nil { link := *z.Key event.Raise(a, &LinkSet{ + Index: link.Index, Rel: link.Rel, Type: link.Type, HRef: link.HRef, @@ -324,6 +330,7 @@ func (a *JRD) OnSubjectSet(subject string, aliases []string, props map[string]*s func (a *JRD) OnLinkSet(o, n *Link) error { modified := false e := &LinkSet{ + Index: n.Index, Rel: n.Rel, Type: n.Type, HRef: n.HRef, @@ -331,39 +338,66 @@ func (a *JRD) OnLinkSet(o, n *Link) error { Properties: n.Properties, } + // if n.Index != o.Index { + // fmt.Println(342) + // modified = true + // } if n.Rel != o.Rel { + fmt.Println(346) modified = true } if n.Type != o.Type { + fmt.Println(350) + modified = true } if n.HRef != o.HRef { + fmt.Println(355) + modified = true } + nKeys := slice.FromMapKeys(n.Properties) + sort.Strings(nKeys) + + oKeys := slice.FromMapKeys(o.Properties) + sort.Strings(oKeys) + for _, z := range slice.Zip( - slice.Zip(slice.FromMap(n.Titles)), - slice.Zip(slice.FromMap(o.Titles)), + slice.Zip(nKeys, slice.FromMapValues(n.Titles, nKeys)), + slice.Zip(oKeys, slice.FromMapValues(o.Titles, oKeys)), ) { if z.Key != z.Value { + fmt.Println(365) + modified = true break } } + nKeys = slice.FromMapKeys(n.Properties) + sort.Strings(nKeys) + + oKeys = slice.FromMapKeys(o.Properties) + sort.Strings(oKeys) + for _, z := range slice.Zip( - slice.Zip(slice.FromMap(n.Properties)), - slice.Zip(slice.FromMap(o.Properties)), + slice.Zip(nKeys, slice.FromMapValues(n.Properties, nKeys)), + slice.Zip(oKeys, slice.FromMapValues(o.Properties, oKeys)), ) { newValue := z.Key curValue := z.Value if newValue.Key != curValue.Key { + fmt.Println(380, newValue.Key, curValue.Key) + modified = true break } if !cmpPtr(newValue.Value, curValue.Value) { + fmt.Println(387) + modified = true break } diff --git a/app/webfinger/jrd_test.go b/app/webfinger/jrd_test.go index 8aeefd0..07037d0 100644 --- a/app/webfinger/jrd_test.go +++ b/app/webfinger/jrd_test.go @@ -111,23 +111,27 @@ func TestApplyEvents(t *testing.T) { }, }, &webfinger.LinkSet{ - Rel: "salty:public", - Type: "application/json+salty", + Index: 0, + Rel: "salty:public", + Type: "application/json+salty", }, &webfinger.LinkSet{ - Rel: "salty:private", - Type: "application/json+salty", + Index: 1, + Rel: "salty:private", + Type: "application/json+salty", }, &webfinger.LinkSet{ - Rel: "salty:public", - Type: "application/json+salty", - HRef: "https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5", + Index: 0, + Rel: "salty:public", + Type: "application/json+salty", + HRef: "https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5", Properties: map[string]*string{ "pub": ptr("kex1r8zshlvkc787pxvauaq7hd6awa9kmheddxjj9k80qmenyxk6284s50uvpw"), }, }, &webfinger.LinkDeleted{ - Rel: "salty:private", + Index: 1, + Rel: "salty:private", }, ) event.SetStreamID(webfinger.StreamID("acct:me@sour.is"), events...) @@ -167,7 +171,6 @@ func TestCommands(t *testing.T) { pub, priv, err := ed25519.GenerateKey(nil) is.NoErr(err) - // fmt.Println(base64.RawURLEncoding.EncodeToString(key)) token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{ "sub": "acct:me@sour.is", "pub": enc(pub), diff --git a/app/webfinger/webfinger.go b/app/webfinger/webfinger.go index 682eff1..52dbcd8 100644 --- a/app/webfinger/webfinger.go +++ b/app/webfinger/webfinger.go @@ -112,6 +112,10 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { return nil, fmt.Errorf("wrong type of claim") } + if c.JRD == nil { + c.JRD = &JRD{} + } + c.JRD.Subject = c.RegisteredClaims.Subject c.SetProperty(NSpubkey, &c.PubKey) @@ -149,9 +153,17 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } + for i := range c.JRD.Links { + c.JRD.Links[i].Index = uint64(i) + } + a, err := ev.Upsert(ctx, s.es, StreamID(c.JRD.Subject), func(ctx context.Context, a *JRD) error { var auth *JRD + for i := range a.Links { + a.Links[i].Index = uint64(i) + } + // does the target have a pubkey for self auth? if _, ok := a.Properties[NSpubkey]; ok { auth = a @@ -237,7 +249,7 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { redirect.Host = u.URL.Host redirect.RawQuery = r.URL.RawQuery redirect.Path = "/.well-known/webfinger" - fmt.Println(redirect) + w.Header().Set("location", redirect.String()) w.WriteHeader(http.StatusSeeOther) return @@ -300,31 +312,32 @@ 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:] - } +// func splitHostPort(hostPort string) (host, port string) { +// host = hostPort - if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { - host = host[1 : len(host)-1] - } +// colon := strings.LastIndexByte(host, ':') +// if colon != -1 && validOptionalPort(host[colon:]) { +// host, port = host[:colon], host[colon+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 -} +// 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 +// } diff --git a/cmd/ev/app.webfinger.go b/cmd/ev/app.webfinger.go index 9a4b966..7d4181f 100644 --- a/cmd/ev/app.webfinger.go +++ b/cmd/ev/app.webfinger.go @@ -38,7 +38,7 @@ var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error cache.SetDefault(s, true) return false }) - var withHostnames webfinger.WithHostnames = strings.Fields(env.Default("WEBFINGER_DOMAINS", "sour.is")) + var withHostnames webfinger.WithHostnames = strings.Fields(env.Default(" ", "sour.is")) wf, err := webfinger.New(ctx, eventstore, withCache, withHostnames) if err != nil { diff --git a/pkg/slice/slice.go b/pkg/slice/slice.go index 34ccd14..48b3f2c 100644 --- a/pkg/slice/slice.go +++ b/pkg/slice/slice.go @@ -44,11 +44,11 @@ func First[T any](in ...T) (T, bool) { } // Map applys func to each element s and returns results as slice. -func Map[T, U any](f func(T) U) func(...T) []U { +func Map[T, U any](f func(int, T) U) func(...T) []U { return func(lis ...T) []U { r := make([]U, len(lis)) for i, v := range lis { - r[i] = f(v) + r[i] = f(i, v) } return r } @@ -73,19 +73,38 @@ func FromMap[K comparable, V any](m map[K]V) (keys []K, values []V) { return nil, nil } + keys = FromMapKeys(m) + return keys, FromMapValues(m, keys) +} + +func FromMapKeys[K comparable, V any](m map[K]V) (keys []K) { + if m == nil { + return nil + } + keys = make([]K, 0, len(m)) - values = make([]V, 0, len(m)) for k := range m { keys = append(keys, k) } + + return keys +} + +func FromMapValues[K comparable, V any](m map[K]V, keys []K) (values []V) { + if m == nil { + return nil + } + + values = make([]V, 0, len(keys)) for _, k := range keys { values = append(values, m[k]) } - return keys, values + return values } + func ToMap[K comparable, V any](keys []K, values []V) (m map[K]V) { m = make(map[K]V, len(keys))