From 54b85678185008a4f6b5778460228788e2dd970d Mon Sep 17 00:00:00 2001 From: Julio Capote Date: Tue, 3 Jan 2023 20:29:50 -0500 Subject: start of keypair model and persisting keypairs --- go.mod | 1 + go.sum | 2 ++ http/router.go | 39 ++++++++++++++++++--------- main.go | 7 +++-- models/keypair.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ registry/registry.go | 35 +++++++++++++++++++++--- sample-cgi-handler.sh | 2 +- sample-config.toml | 22 +++++++-------- views/outbox.go | 2 +- 9 files changed, 153 insertions(+), 31 deletions(-) create mode 100644 models/keypair.go diff --git a/go.mod b/go.mod index c8e5410..a6b76bb 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.8.1 // indirect github.com/go-fed/activity v1.0.0 // indirect + github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect diff --git a/go.sum b/go.sum index faf8240..b80dceb 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR github.com/go-fed/activity v1.0.0 h1:j7w3auHZnVCjUcgA1mE+UqSOjFBhvW2Z2res3vNol+o= github.com/go-fed/activity v1.0.0/go.mod h1:v4QoPaAzjWZ8zN2VFVGL5ep9C02mst0hQYHUpQwso4Q= github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE= +github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= +github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= diff --git a/http/router.go b/http/router.go index a34ff7c..be2a128 100644 --- a/http/router.go +++ b/http/router.go @@ -1,19 +1,22 @@ package http import ( + "bytes" "io" "net/http" "git.capotej.com/capotej/communique/registry" "github.com/gin-gonic/gin" + "go.uber.org/zap" ) type Router struct { registry *registry.Registry + log *zap.SugaredLogger } -func NewRouter(registry *registry.Registry) *Router { - return &Router{registry: registry} +func NewRouter(registry *registry.Registry, log *zap.SugaredLogger) *Router { + return &Router{registry: registry, log: log} } func render(c *gin.Context, resource map[string]interface{}, err error) { @@ -68,17 +71,27 @@ func (s *Router) Start(zapWriter io.Writer) { render(c, resource, err) }) - // // Inbox - // router.POST("/actors/:actor/inbox", func(c *gin.Context) { - // actorParam := c.Param("actor") - // resource := s.registry.Inbox(actorParam) - // if resource != nil { - // c.JSON(http.StatusOK, resource) - // } else { - // c.JSON(http.StatusNotFound, nil) - // } - - // }) + // Inbox + router.POST("/actors/:actor/inbox", func(c *gin.Context) { + log := s.log.With("type", "inbox") + buf := new(bytes.Buffer) + buf.ReadFrom(c.Request.Body) + payload := buf.String() + log.With( + "payload", + c.GetHeader("Signature"), + ).With( + "content-type", + c.GetHeader("Content-Type"), + ).With( + "payload", + payload, + ).Debug("received item") + actorParam := c.Param("actor") + err := s.registry.Inbox(actorParam, c.Request) + resource := map[string]interface{}{} + render(c, resource, err) + }) // Actor Outbox router.GET("/actors/:actor/outbox", func(c *gin.Context) { diff --git a/main.go b/main.go index e65a600..453a4e3 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,10 @@ func main() { persister := models.NewPersister(log, db) // Registry - registry := registry.NewRegistry(cfg, persister) + registry, err := registry.NewRegistry(cfg, persister) + if err != nil { + log.Fatal(err) + } // Servers var mainWg sync.WaitGroup @@ -64,7 +67,7 @@ func main() { // // External Http Server writer := &zapio.Writer{Log: logger, Level: zap.DebugLevel} defer writer.Close() - router := http.NewRouter(registry) + router := http.NewRouter(registry, log) mainWg.Add(1) go router.Start(writer) diff --git a/models/keypair.go b/models/keypair.go new file mode 100644 index 0000000..bff60cd --- /dev/null +++ b/models/keypair.go @@ -0,0 +1,74 @@ +package models + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "encoding/gob" + "fmt" + "time" + + "git.capotej.com/capotej/communique/config" + "github.com/dgraph-io/badger/v3" +) + +type Keypair struct { + Handler config.Handler + PrivateKey rsa.PrivateKey + CreatedAt time.Time +} + +// used for lookup purposes (count, collect, find) +func NewKeypair(h config.Handler) *Keypair { + aso := &Keypair{Handler: h} + return aso +} + +func CreateKeypair(h config.Handler) (*Keypair, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, fmt.Errorf("could not generate private key for %s: %w", h.Name, err) + } + aso := &Keypair{ + PrivateKey: *key, + Handler: h, + } + return aso, nil +} + +func (a *Keypair) Name() string { + return "Keypair" +} + +func (a *Keypair) Key() string { + keyBase := fmt.Sprintf("keypairs:%s", a.Handler.Name) + return keyBase +} + +func (a *Keypair) DedupKey() string { + return a.Key() +} + +func (a *Keypair) Keybase() string { + return a.Key() +} + +func (a *Keypair) SaveDedup(txn *badger.Txn) error { + txn.Discard() // nothing to do here + return nil +} + +func (a *Keypair) Save(txn *badger.Txn) error { + if a.PrivateKey.D == nil { + return fmt.Errorf("private key not set") + } + + var network bytes.Buffer + enc := gob.NewEncoder(&network) + err := enc.Encode(a) + if err != nil { + return fmt.Errorf("could not encode keypair: %w", err) + } + e := badger.NewEntry([]byte(a.Key()), network.Bytes()) + return txn.SetEntry(e) +} diff --git a/registry/registry.go b/registry/registry.go index 16ae828..bbe3f1c 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -3,6 +3,7 @@ package registry import ( "bytes" "encoding/gob" + "net/http" "net/url" "strings" @@ -23,13 +24,29 @@ type Registry struct { handlerMap map[string]Handler } -func NewRegistry(cfg config.Config, persister *models.Persister) *Registry { +func NewRegistry(cfg config.Config, persister *models.Persister) (*Registry, error) { reg := Registry{cfg: cfg, persister: persister} reg.handlerMap = make(map[string]Handler) for _, v := range cfg.Handlers { reg.handlerMap[v.Name] = Handler{handlerCfg: v} + err := generateKeypairIfNeeded(v, persister) + if err != nil { + return nil, err + } } - return ® + return ®, nil +} + +func generateKeypairIfNeeded(v config.Handler, p *models.Persister) error { + kp, err := models.CreateKeypair(v) + if err != nil { + return err + } + err = p.Store(kp) + if err != nil { + return err + } + return nil } func (r *Registry) Actor(name string) (map[string]interface{}, error) { @@ -51,7 +68,7 @@ func (r *Registry) OutboxCollection(name string) (map[string]interface{}, error) return nil, err } var outboxItems []models.OutboxItem - for _, v := range page[0:20] { //TODO pagination + for _, v := range page { //TODO pagination buf := bytes.NewBuffer(v) dec := gob.NewDecoder(buf) var outboxItem models.OutboxItem @@ -109,6 +126,18 @@ func (r *Registry) Followers(name string) (map[string]interface{}, error) { return result, nil } +func (r *Registry) Inbox(name string, req *http.Request) error { + handler := r.findByName(name) + if handler == nil { + return nil + } + // verifier, err := httpsig.NewVerifier(req) + // if err != nil { + // return err + // } + return nil +} + func (r *Registry) ActivityOrNote(activityOrNote, name, id string) (map[string]interface{}, error) { handler := r.findByName(name) if handler == nil { diff --git a/sample-cgi-handler.sh b/sample-cgi-handler.sh index bc82dc4..11bae71 100755 --- a/sample-cgi-handler.sh +++ b/sample-cgi-handler.sh @@ -3,7 +3,7 @@ echo "Content-type: application/atom+xml" echo "" echo ' - alkfjlasjf + alkfjaiasdfaslasjf ' exit 0 diff --git a/sample-config.toml b/sample-config.toml index 293a525..f659e60 100644 --- a/sample-config.toml +++ b/sample-config.toml @@ -1,19 +1,19 @@ domain = "http://localhost:8080" dbPath = "bub.db" -# [[handlers]] -# name = "sample" -# rpc = "cgi" # rename to protocol? -# exec = "sample-cgi-handler.sh" -# interval = "5s" -# dedupWindow = "1m" - [[handlers]] -name = "autosport" +name = "sample" rpc = "cgi" # rename to protocol? -exec = "autosport-handler.sh" -interval = "10m" -dedupWindow = "900h" +exec = "sample-cgi-handler.sh" +interval = "5s" +dedupWindow = "1m" + +# [[handlers]] +# name = "autosport" +# rpc = "cgi" # rename to protocol? +# exec = "autosport-handler.sh" +# interval = "10m" +# dedupWindow = "900h" # [[handlers]] # name = "another" diff --git a/views/outbox.go b/views/outbox.go index 6132072..37afed1 100644 --- a/views/outbox.go +++ b/views/outbox.go @@ -55,7 +55,7 @@ func RenderOutboxCollection(name, domain string, page []models.OutboxItem) (map[ itemsProp.AppendActivityStreamsCreate(crea) } totalProp := streams.NewActivityStreamsTotalItemsProperty() - totalProp.Set(20) + totalProp.Set(len(page)) oc.SetActivityStreamsTotalItems(totalProp) oc.SetActivityStreamsOrderedItems(itemsProp) return streams.Serialize(oc) -- cgit v1.2.3