feat: allow multiple rel with matching value

This commit is contained in:
Jon Lundy 2023-04-19 17:53:00 -06:00
parent b8c2f9f510
commit 12a3e7b1ff
Signed by untrusted user who does not match committer: xuu
GPG Key ID: C63E6D61F3035024
7 changed files with 134 additions and 67 deletions

View File

@ -35,7 +35,3 @@ ifeq (, $(shell which gqlgen))
endif endif
gqlgen gqlgen
EV_HOST?=localhost:8080
load:
watch -n .1 "http POST $(EV_HOST)/inbox/asdf/test a=b one=1 two:='{\"v\":2}' | jq"

View File

@ -9,7 +9,7 @@ type SubjectSet struct {
Aliases []string `json:"aliases,omitempty"` Aliases []string `json:"aliases,omitempty"`
Properties map[string]*string `json:"properties,omitempty"` Properties map[string]*string `json:"properties,omitempty"`
event.IsEvent event.IsEvent `json:"-"`
} }
var _ event.Event = (*SubjectSet)(nil) var _ event.Event = (*SubjectSet)(nil)
@ -17,27 +17,29 @@ var _ event.Event = (*SubjectSet)(nil)
type SubjectDeleted struct { type SubjectDeleted struct {
Subject string `json:"subject"` Subject string `json:"subject"`
event.IsEvent event.IsEvent `json:"-"`
} }
var _ event.Event = (*SubjectDeleted)(nil) var _ event.Event = (*SubjectDeleted)(nil)
type LinkSet struct { type LinkSet struct {
Index uint64 `json:"idx"`
Rel string `json:"rel"` Rel string `json:"rel"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
HRef string `json:"href,omitempty"` HRef string `json:"href,omitempty"`
Titles map[string]string `json:"titles,omitempty"` Titles map[string]string `json:"titles,omitempty"`
Properties map[string]*string `json:"properties,omitempty"` Properties map[string]*string `json:"properties,omitempty"`
event.IsEvent event.IsEvent `json:"-"`
} }
var _ event.Event = (*LinkSet)(nil) var _ event.Event = (*LinkSet)(nil)
type LinkDeleted struct { 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) var _ event.Event = (*LinkDeleted)(nil)

View File

@ -23,13 +23,12 @@ func StreamID(subject string) string {
// JRD is a JSON Resource Descriptor, specifying properties and related links // JRD is a JSON Resource Descriptor, specifying properties and related links
// for a resource. // for a resource.
type JRD struct { type JRD struct {
Subject string `json:"subject,omitempty" yaml:"subject,omitempty"` Subject string `json:"subject,omitempty" yaml:"subject,omitempty"`
Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"` Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"`
Properties map[string]*string `json:"properties,omitempty" yaml:"properties,omitempty"` Properties map[string]*string `json:"properties,omitempty" yaml:"properties,omitempty"`
Links Links `json:"links,omitempty" yaml:"links,omitempty"` Links Links `json:"links,omitempty" yaml:"links,omitempty"`
deleted bool deleted bool
event.IsAggregate `yaml:"-"` event.IsAggregate `json:"-" yaml:"-"`
} }
func (a *JRD) CloneValues() *JRD { func (a *JRD) CloneValues() *JRD {
@ -49,6 +48,7 @@ var _ event.Aggregate = (*JRD)(nil)
// Link is a link to a related resource. // Link is a link to a related resource.
type Link struct { type Link struct {
Index uint64 `json:"-" yaml:"-"`
Rel string `json:"rel,omitempty"` Rel string `json:"rel,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
HRef string `json:"href,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 { if l[i] == nil || l[j] == nil {
return false return false
} }
if l[i].Rel == l[j].Rel {
return l[i].Type < l[j].Type
}
return l[i].Rel < l[j].Rel return l[i].Rel < l[j].Rel
} }
@ -89,6 +92,9 @@ func ParseJRD(blob []byte) (*JRD, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for i := range jrd.Links {
jrd.Links[i].Index = uint64(i)
}
return &jrd, nil return &jrd, nil
} }
@ -102,7 +108,7 @@ func (jrd *JRD) GetLinkByRel(rel string) *Link {
return nil 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 { func (jrd *JRD) GetLinksByRel(rel ...string) []*Link {
var lis []*Link var lis []*Link
rels := set.New(rel...) rels := set.New(rel...)
@ -180,20 +186,21 @@ func (a *JRD) ApplyEvent(events ...event.Event) {
a.Properties = map[string]*string{} a.Properties = map[string]*string{}
case *LinkSet: 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 { if !ok {
link = &Link{} link = &Link{}
link.Rel = e.Rel link.Index = uint64(len(a.Links))
a.Links = append(a.Links, link) a.Links = append(a.Links, link)
} }
link.Rel = e.Rel
link.HRef = e.HRef link.HRef = e.HRef
link.Type = e.Type link.Type = e.Type
link.Titles = e.Titles link.Titles = e.Titles
link.Properties = e.Properties link.Properties = e.Properties
case *LinkDeleted: 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 return err
} }
sort.Sort(jrd.Links)
sort.Sort(a.Links)
for _, z := range slice.Align( for _, z := range slice.Align(
jrd.Links, jrd.Links,
a.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 // Not in new == delete
if z.Key == nil { if z.Key == nil {
link := *z.Value link := *z.Value
event.Raise(a, &LinkDeleted{Rel: link.Rel}) event.Raise(a, &LinkDeleted{Index: link.Index, Rel: link.Rel})
continue continue
} }
@ -259,6 +264,7 @@ func (a *JRD) OnClaims(jrd *JRD) error {
if z.Value == nil { if z.Value == nil {
link := *z.Key link := *z.Key
event.Raise(a, &LinkSet{ event.Raise(a, &LinkSet{
Index: link.Index,
Rel: link.Rel, Rel: link.Rel,
Type: link.Type, Type: link.Type,
HRef: link.HRef, 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 { func (a *JRD) OnLinkSet(o, n *Link) error {
modified := false modified := false
e := &LinkSet{ e := &LinkSet{
Index: n.Index,
Rel: n.Rel, Rel: n.Rel,
Type: n.Type, Type: n.Type,
HRef: n.HRef, HRef: n.HRef,
@ -331,39 +338,66 @@ func (a *JRD) OnLinkSet(o, n *Link) error {
Properties: n.Properties, Properties: n.Properties,
} }
// if n.Index != o.Index {
// fmt.Println(342)
// modified = true
// }
if n.Rel != o.Rel { if n.Rel != o.Rel {
fmt.Println(346)
modified = true modified = true
} }
if n.Type != o.Type { if n.Type != o.Type {
fmt.Println(350)
modified = true modified = true
} }
if n.HRef != o.HRef { if n.HRef != o.HRef {
fmt.Println(355)
modified = true modified = true
} }
nKeys := slice.FromMapKeys(n.Properties)
sort.Strings(nKeys)
oKeys := slice.FromMapKeys(o.Properties)
sort.Strings(oKeys)
for _, z := range slice.Zip( for _, z := range slice.Zip(
slice.Zip(slice.FromMap(n.Titles)), slice.Zip(nKeys, slice.FromMapValues(n.Titles, nKeys)),
slice.Zip(slice.FromMap(o.Titles)), slice.Zip(oKeys, slice.FromMapValues(o.Titles, oKeys)),
) { ) {
if z.Key != z.Value { if z.Key != z.Value {
fmt.Println(365)
modified = true modified = true
break break
} }
} }
nKeys = slice.FromMapKeys(n.Properties)
sort.Strings(nKeys)
oKeys = slice.FromMapKeys(o.Properties)
sort.Strings(oKeys)
for _, z := range slice.Zip( for _, z := range slice.Zip(
slice.Zip(slice.FromMap(n.Properties)), slice.Zip(nKeys, slice.FromMapValues(n.Properties, nKeys)),
slice.Zip(slice.FromMap(o.Properties)), slice.Zip(oKeys, slice.FromMapValues(o.Properties, oKeys)),
) { ) {
newValue := z.Key newValue := z.Key
curValue := z.Value curValue := z.Value
if newValue.Key != curValue.Key { if newValue.Key != curValue.Key {
fmt.Println(380, newValue.Key, curValue.Key)
modified = true modified = true
break break
} }
if !cmpPtr(newValue.Value, curValue.Value) { if !cmpPtr(newValue.Value, curValue.Value) {
fmt.Println(387)
modified = true modified = true
break break
} }

View File

@ -111,23 +111,27 @@ func TestApplyEvents(t *testing.T) {
}, },
}, },
&webfinger.LinkSet{ &webfinger.LinkSet{
Rel: "salty:public", Index: 0,
Type: "application/json+salty", Rel: "salty:public",
Type: "application/json+salty",
}, },
&webfinger.LinkSet{ &webfinger.LinkSet{
Rel: "salty:private", Index: 1,
Type: "application/json+salty", Rel: "salty:private",
Type: "application/json+salty",
}, },
&webfinger.LinkSet{ &webfinger.LinkSet{
Rel: "salty:public", Index: 0,
Type: "application/json+salty", Rel: "salty:public",
HRef: "https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5", Type: "application/json+salty",
HRef: "https://ev.sour.is/inbox/01GAEMKXYJ4857JQP1MJGD61Z5",
Properties: map[string]*string{ Properties: map[string]*string{
"pub": ptr("kex1r8zshlvkc787pxvauaq7hd6awa9kmheddxjj9k80qmenyxk6284s50uvpw"), "pub": ptr("kex1r8zshlvkc787pxvauaq7hd6awa9kmheddxjj9k80qmenyxk6284s50uvpw"),
}, },
}, },
&webfinger.LinkDeleted{ &webfinger.LinkDeleted{
Rel: "salty:private", Index: 1,
Rel: "salty:private",
}, },
) )
event.SetStreamID(webfinger.StreamID("acct:me@sour.is"), events...) event.SetStreamID(webfinger.StreamID("acct:me@sour.is"), events...)
@ -167,7 +171,6 @@ func TestCommands(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(nil) pub, priv, err := ed25519.GenerateKey(nil)
is.NoErr(err) is.NoErr(err)
// fmt.Println(base64.RawURLEncoding.EncodeToString(key))
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{ token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, jwt.MapClaims{
"sub": "acct:me@sour.is", "sub": "acct:me@sour.is",
"pub": enc(pub), "pub": enc(pub),

View File

@ -112,6 +112,10 @@ 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")
} }
if c.JRD == nil {
c.JRD = &JRD{}
}
c.JRD.Subject = c.RegisteredClaims.Subject c.JRD.Subject = c.RegisteredClaims.Subject
c.SetProperty(NSpubkey, &c.PubKey) 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 { a, err := ev.Upsert(ctx, s.es, StreamID(c.JRD.Subject), func(ctx context.Context, a *JRD) error {
var auth *JRD var auth *JRD
for i := range a.Links {
a.Links[i].Index = uint64(i)
}
// does the target have a pubkey for self auth? // does the target have a pubkey for self auth?
if _, ok := a.Properties[NSpubkey]; ok { if _, ok := a.Properties[NSpubkey]; ok {
auth = a auth = a
@ -237,7 +249,7 @@ func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
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"
fmt.Println(redirect)
w.Header().Set("location", redirect.String()) w.Header().Set("location", redirect.String())
w.WriteHeader(http.StatusSeeOther) w.WriteHeader(http.StatusSeeOther)
return return
@ -300,31 +312,32 @@ 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, ':') // func splitHostPort(hostPort string) (host, port string) {
if colon != -1 && validOptionalPort(host[colon:]) { // host = hostPort
host, port = host[:colon], host[colon+1:]
}
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { // colon := strings.LastIndexByte(host, ':')
host = host[1 : len(host)-1] // if colon != -1 && validOptionalPort(host[colon:]) {
} // host, port = host[:colon], host[colon+1:]
// }
return // if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
} // host = host[1 : len(host)-1]
func validOptionalPort(port string) bool { // }
if port == "" {
return true // return
} // }
if port[0] != ':' { // func validOptionalPort(port string) bool {
return false // if port == "" {
} // return true
for _, b := range port[1:] { // }
if b < '0' || b > '9' { // if port[0] != ':' {
return false // return false
} // }
} // for _, b := range port[1:] {
return true // if b < '0' || b > '9' {
} // return false
// }
// }
// return true
// }

View File

@ -38,7 +38,7 @@ var _ = apps.Register(50, func(ctx context.Context, svc *service.Harness) error
cache.SetDefault(s, true) cache.SetDefault(s, true)
return false 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) wf, err := webfinger.New(ctx, eventstore, withCache, withHostnames)
if err != nil { if err != nil {

View File

@ -44,11 +44,11 @@ func First[T any](in ...T) (T, bool) {
} }
// Map applys func to each element s and returns results as slice. // 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 { return func(lis ...T) []U {
r := make([]U, len(lis)) r := make([]U, len(lis))
for i, v := range lis { for i, v := range lis {
r[i] = f(v) r[i] = f(i, v)
} }
return r return r
} }
@ -73,19 +73,38 @@ func FromMap[K comparable, V any](m map[K]V) (keys []K, values []V) {
return nil, nil 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)) keys = make([]K, 0, len(m))
values = make([]V, 0, len(m))
for k := range m { for k := range m {
keys = append(keys, k) 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 { for _, k := range keys {
values = append(values, m[k]) values = append(values, m[k])
} }
return keys, values return values
} }
func ToMap[K comparable, V any](keys []K, values []V) (m map[K]V) { func ToMap[K comparable, V any](keys []K, values []V) (m map[K]V) {
m = make(map[K]V, len(keys)) m = make(map[K]V, len(keys))