aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulio Capote <jcapote@gmail.com>2023-01-05 02:15:57 +0000
committerJulio Capote <jcapote@gmail.com>2023-01-05 02:15:57 +0000
commite9124914cd6b05502cb5dd7f34720b27657560aa (patch)
tree72c68e673a88e8bd88d02ff22c80d0d17ccb8b88
parent6736b04552c582ab5a07556ba20eca92612daf51 (diff)
downloadcommunique-e9124914cd6b05502cb5dd7f34720b27657560aa.tar.gz
refactor verification into verifier tool, start follow/undo
-rw-r--r--http/router.go2
-rw-r--r--registry/registry.go79
-rw-r--r--tools/verifier.go81
3 files changed, 107 insertions, 55 deletions
diff --git a/http/router.go b/http/router.go
index 456ed27..fcbd7d2 100644
--- a/http/router.go
+++ b/http/router.go
@@ -88,7 +88,7 @@ func (s *Router) Start(zapWriter io.Writer) {
payload,
).Debug("received item")
actorParam := c.Param("actor")
- err := s.registry.Inbox(actorParam, c.Request)
+ err := s.registry.Inbox(actorParam, c.Request, buf.Bytes())
resource := map[string]interface{}{}
render(c, resource, err)
})
diff --git a/registry/registry.go b/registry/registry.go
index 6f47f8a..aca1db7 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -7,19 +7,17 @@ import (
"encoding/gob"
"encoding/json"
"encoding/pem"
- "fmt"
- "io"
"net/http"
"net/url"
"strings"
"git.capotej.com/capotej/communique/config"
"git.capotej.com/capotej/communique/models"
+ "git.capotej.com/capotej/communique/tools"
"git.capotej.com/capotej/communique/urls"
"git.capotej.com/capotej/communique/views"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
- "github.com/go-fed/httpsig"
"go.uber.org/zap"
)
@@ -157,81 +155,54 @@ func (r *Registry) Followers(name string) (map[string]interface{}, error) {
return result, nil
}
-func (r *Registry) Inbox(name string, req *http.Request) error {
+func (r *Registry) Inbox(name string, req *http.Request, payload []byte) error {
handler := r.findByName(name)
if handler == nil {
return nil
}
- logger := r.log.With("type", "inbox")
domainUrl, err := url.Parse(r.cfg.Domain)
if err != nil {
return err
}
+ logger := r.log.With("type", "inbox")
+
if req.Host != domainUrl.Host {
logger.Warnf("%s != %s, configured domain should match incoming Host: header otherwise things may not work right. You'll need to enable 'ProxyPreserveHost' (for apache2) or proxy_set_header Host $host; (for nginx)", req.Host, r.cfg.Domain)
}
- verifier, err := httpsig.NewVerifier(req)
- if err != nil {
- return err
- }
-
- keyId := verifier.KeyId()
- logger.With("keyId", keyId).Debugf("fetching")
- req, err = http.NewRequest("GET", keyId, nil)
- req.Header.Set("Accept", "application/json; charset=UTF-8")
+ ctx := context.Background()
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- return err
+ person, verifyError := tools.VerifyRequest(ctx, req, r.log)
+ if verifyError != nil {
+ return verifyError
}
- defer resp.Body.Close()
- keyPage, err := io.ReadAll(resp.Body)
- logger.With("keyId", keyId).With("response", string(keyPage)).Debugf("received response")
- var keyPageData map[string]interface{}
- err = json.Unmarshal(keyPage, &keyPageData)
+
+ var followData map[string]interface{}
+ err = json.Unmarshal(payload, &followData)
if err != nil {
return err
}
- var person vocab.ActivityStreamsPerson
-
- resolver, err := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsPerson) error {
- // Store the person in the enclosing scope, for later.
- person = p
+ resolver, err := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsFollow) error {
+ idProp := person.GetJSONLDId()
+ idPropUrl := idProp.Get()
+ inboxProp := person.GetActivityStreamsInbox()
+ url := inboxProp.GetIRI()
+ logger.With("actor", idPropUrl).With("inbox", url).Debugf("follow")
return nil
- }, func(c context.Context, note vocab.ActivityStreamsNote) error {
- //TODO not needed, need to figure out how to only pass one func
+ }, func(c context.Context, note vocab.ActivityStreamsUndo) error {
+ idProp := person.GetJSONLDId()
+ idPropUrl := idProp.Get()
+ inboxProp := person.GetActivityStreamsInbox()
+ url := inboxProp.GetIRI()
+ logger.With("actor", idPropUrl).With("inbox", url).Debugf("undo")
return nil
})
+ err = resolver.Resolve(ctx, followData)
- ctx := context.Background()
- err = resolver.Resolve(ctx, keyPageData)
-
- pubKeyProp := person.GetW3IDSecurityV1PublicKey()
- iter := pubKeyProp.At(0) // TODO not safe
- pubKey := iter.Get()
- pemProp := pubKey.GetW3IDSecurityV1PublicKeyPem()
- pemStr := pemProp.Get()
- logger.With("keyId", keyId).With("pem", pemStr).Debugf("extracted pem")
- pemObj, _ := pem.Decode([]byte(pemStr))
- if pemObj == nil {
- return fmt.Errorf("no PEM block found")
- }
- if pemObj.Type != "PUBLIC KEY" {
- return fmt.Errorf("no public key found in PEM block")
- }
-
- decodedKey, err := x509.ParsePKIXPublicKey(pemObj.Bytes)
- if err != nil {
- return err
- }
- logger.With("keyId", keyId).With("pem", pemStr).Debugf("got %T", decodedKey)
- algo := httpsig.RSA_SHA256
- return verifier.Verify(decodedKey, algo)
+ return nil
}
func (r *Registry) ActivityOrNote(activityOrNote, name, id string) (map[string]interface{}, error) {
diff --git a/tools/verifier.go b/tools/verifier.go
new file mode 100644
index 0000000..4063ccf
--- /dev/null
+++ b/tools/verifier.go
@@ -0,0 +1,81 @@
+package tools
+
+import (
+ "context"
+ "crypto/x509"
+ "encoding/json"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/go-fed/activity/streams"
+ "github.com/go-fed/activity/streams/vocab"
+ "github.com/go-fed/httpsig"
+ "go.uber.org/zap"
+)
+
+func VerifyRequest(ctx context.Context, req *http.Request, log *zap.SugaredLogger) (vocab.ActivityStreamsPerson, error) {
+ logger := log.With("type", "verifier")
+ verifier, err := httpsig.NewVerifier(req)
+ if err != nil {
+ return nil, err
+ }
+
+ keyId := verifier.KeyId()
+ logger.With("keyId", keyId).Debugf("fetching key page")
+ req, err = http.NewRequest("GET", keyId, nil)
+ req.Header.Set("Accept", "application/json; charset=UTF-8")
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ keyPage, err := io.ReadAll(resp.Body)
+ logger.With("keyId", keyId).With("response", string(keyPage)).Debugf("received response")
+ var keyPageData map[string]interface{}
+ err = json.Unmarshal(keyPage, &keyPageData)
+ if err != nil {
+ return nil, err
+ }
+
+ var person vocab.ActivityStreamsPerson
+
+ resolver, err := streams.NewJSONResolver(func(c context.Context, p vocab.ActivityStreamsPerson) error {
+ // Store the person in the enclosing scope, for later.
+ person = p
+ return nil
+ }, func(c context.Context, note vocab.ActivityStreamsNote) error {
+ //TODO not needed, need to figure out how to only pass one func
+ return nil
+ })
+
+ err = resolver.Resolve(ctx, keyPageData)
+ if err != nil {
+ return nil, err
+ }
+
+ pubKeyProp := person.GetW3IDSecurityV1PublicKey()
+ iter := pubKeyProp.At(0) // TODO not safe, use a for instead
+ pubKey := iter.Get()
+ pemProp := pubKey.GetW3IDSecurityV1PublicKeyPem()
+ pemStr := pemProp.Get()
+ logger.With("keyId", keyId).With("pem", pemStr).Debugf("extracted pem")
+ pemObj, _ := pem.Decode([]byte(pemStr))
+ if pemObj == nil {
+ return nil, fmt.Errorf("no PEM block found")
+ }
+ if pemObj.Type != "PUBLIC KEY" {
+ return nil, fmt.Errorf("no public key found in PEM block")
+ }
+
+ decodedKey, err := x509.ParsePKIXPublicKey(pemObj.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ logger.With("keyId", keyId).With("pem", pemStr).Debugf("got %T", decodedKey)
+ algo := httpsig.RSA_SHA256
+ return person, verifier.Verify(decodedKey, algo)
+}