mirror of https://gitlab.com/ngerakines/tavern.git
246 lines
5.5 KiB
Go
246 lines
5.5 KiB
Go
package fed
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ngerakines/tavern/common"
|
|
"github.com/ngerakines/tavern/storage"
|
|
)
|
|
|
|
type ActorClient struct {
|
|
HTTPClient common.HTTPClient
|
|
Logger *zap.Logger
|
|
Storage storage.Storage
|
|
}
|
|
|
|
func (client ActorClient) Get(location string) (string, storage.Payload, error) {
|
|
client.Logger.Debug("Sending actor request", zap.String("url", location))
|
|
request, err := http.NewRequest("GET", location, nil)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
request.Header.Add("Accept", "application/ld+json")
|
|
|
|
resp, err := client.HTTPClient.Do(request)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return "", nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
}
|
|
body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1*1024*1024))
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
p, err := storage.PayloadFromBytes(body)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
|
|
// return storage.PayloadFromReader(io.LimitReader(resp.Body, 1*1024*1024))
|
|
return string(body), p, nil
|
|
}
|
|
|
|
func GetOrFetchActor(ctx context.Context, store storage.Storage, logger *zap.Logger, httpClient common.HTTPClient, hint string) (*storage.Actor, error) {
|
|
|
|
wfc := WebFingerClient{
|
|
HTTPClient: httpClient,
|
|
Logger: logger,
|
|
}
|
|
|
|
if strings.HasPrefix(hint, "https://") {
|
|
count, err := store.RowCount(ctx, `SELECT COUNT(*) FROM actors WHERE actor_id = $1`, hint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if count > 0 {
|
|
return store.GetActorByActorID(ctx, hint)
|
|
}
|
|
|
|
} else {
|
|
count, err := store.RowCount(ctx, `SELECT COUNT(*) FROM actor_aliases WHERE alias = $1`, strings.TrimPrefix(hint, "@"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if count > 0 {
|
|
return store.GetActorByAlias(ctx, hint)
|
|
}
|
|
}
|
|
|
|
wfp, err := wfc.Fetch(hint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var actorURL string
|
|
|
|
subject, hasSubject := storage.JSONString(wfp, "subject")
|
|
if !hasSubject {
|
|
return nil, fmt.Errorf("webfinger request did not contain subject")
|
|
}
|
|
|
|
links, hasLinks := storage.JSONMapList(wfp, "links")
|
|
if hasLinks {
|
|
for _, link := range links {
|
|
rel, hasRel := storage.JSONString(link, "rel")
|
|
href, hasHref := storage.JSONString(link, "href")
|
|
if hasRel && hasHref && rel == "self" {
|
|
actorURL = href
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(actorURL) == 0 {
|
|
return nil, fmt.Errorf("webfinger response did not contain self link for actor")
|
|
}
|
|
|
|
logger.Debug("parsed actor id from webfinger payload",
|
|
zap.String("actor", actorURL),
|
|
zap.String("subject", subject))
|
|
|
|
ac := ActorClient{
|
|
HTTPClient: httpClient,
|
|
Logger: logger,
|
|
}
|
|
|
|
_, actorPayload, err := ac.Get(actorURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actorID, ok := storage.JSONString(actorPayload, "id")
|
|
if !ok {
|
|
return nil, fmt.Errorf("no id found for actor")
|
|
}
|
|
|
|
keyID, keyPEM, err := storage.KeyFromActor(actorPayload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = store.CreateActor(ctx, actorID, actorPayload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actorRowID, err := store.ActorRowIDForActorID(ctx, actorID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = store.RecordActorAlias(ctx, actorRowID, subject, storage.ActorAliasSubject); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hasLinks {
|
|
for _, link := range links {
|
|
rel, hasRel := storage.JSONString(link, "rel")
|
|
href, hasHref := storage.JSONString(link, "href")
|
|
if !hasRel || !hasHref {
|
|
continue
|
|
}
|
|
switch rel {
|
|
case "self":
|
|
if err = store.RecordActorAlias(ctx, actorRowID, href, storage.ActorAliasSelf); err != nil {
|
|
return nil, err
|
|
}
|
|
case "http://webfinger.net/rel/profile-page":
|
|
if err = store.RecordActorAlias(ctx, actorRowID, href, storage.ActorAliasProfilePage); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
err = store.RecordActorKey(ctx, actorRowID, keyID, keyPEM)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if endpoints, ok := storage.JSONMap(actorPayload, "endpoints"); ok {
|
|
// sharedInbox, ok := storage.JSONString(endpoints, "sharedInbox")
|
|
// if ok {
|
|
// peerID := storage.NewV4()
|
|
// err = store.CreatePeer(ctx, peerID, sharedInbox)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
return store.GetActor(ctx, actorRowID)
|
|
}
|
|
|
|
func ActorsFromActivity(activity storage.Payload) []string {
|
|
var actors []string
|
|
|
|
actor, ok := storage.JSONString(activity, "actor")
|
|
if ok {
|
|
actors = append(actors, actor)
|
|
}
|
|
|
|
obj, ok := storage.JSONMap(activity, "object")
|
|
if !ok {
|
|
obj = activity
|
|
}
|
|
|
|
if ok {
|
|
attributedTo, ok := storage.JSONString(obj, "attributedTo")
|
|
if ok {
|
|
actors = append(actors, attributedTo)
|
|
}
|
|
|
|
tag, ok := storage.JSONMapList(obj, "tag")
|
|
if ok {
|
|
for _, i := range tag {
|
|
tagType, hasTagType := storage.JSONString(i, "type")
|
|
href, hasHref := storage.JSONString(i, "href")
|
|
if hasTagType && hasHref && tagType == "Mention" {
|
|
actors = append(actors, href)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return actors
|
|
}
|
|
|
|
func ActorsFromObject(obj storage.Payload) []string {
|
|
var actors []string
|
|
|
|
actor, ok := storage.JSONString(obj, "actor")
|
|
if ok {
|
|
actors = append(actors, actor)
|
|
}
|
|
|
|
attributedTo, ok := storage.JSONString(obj, "attributedTo")
|
|
if ok {
|
|
actors = append(actors, attributedTo)
|
|
}
|
|
|
|
if ok {
|
|
tag, ok := storage.JSONMapList(obj, "tag")
|
|
if ok {
|
|
for _, i := range tag {
|
|
tagType, hasTagType := storage.JSONString(i, "type")
|
|
href, hasHref := storage.JSONString(i, "href")
|
|
if hasTagType && hasHref && tagType == "Mention" {
|
|
actors = append(actors, href)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return actors
|
|
}
|