mirror of
				https://github.com/owncast/owncast.git
				synced 2025-11-04 13:27:21 +08:00 
			
		
		
		
	* add GetResolvedPublicKeyFromIRI * verify public key using key not actor w/key * try most common algos first * try stated algo first * make sure not to try an algo twice * return errors per algorithm * linting
		
			
				
	
	
		
			237 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package resolvers
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/json"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
 | 
						|
	"github.com/go-fed/activity/streams"
 | 
						|
	"github.com/go-fed/activity/streams/vocab"
 | 
						|
	"github.com/owncast/owncast/activitypub/apmodels"
 | 
						|
	"github.com/owncast/owncast/activitypub/crypto"
 | 
						|
	"github.com/owncast/owncast/core/data"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
// Resolve will translate a raw ActivityPub payload and fire the callback associated with that activity type.
 | 
						|
func Resolve(c context.Context, data []byte, callbacks ...interface{}) error {
 | 
						|
	jsonResolver, err := streams.NewJSONResolver(callbacks...)
 | 
						|
	if err != nil {
 | 
						|
		// Something in the setup was wrong. For example, a callback has an
 | 
						|
		// unsupported signature and would never be called
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var jsonMap map[string]interface{}
 | 
						|
	if err = json.Unmarshal(data, &jsonMap); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	log.Debugln("Resolving payload...", string(data))
 | 
						|
 | 
						|
	// The createCallback function will be called.
 | 
						|
	err = jsonResolver.Resolve(c, jsonMap)
 | 
						|
	if err != nil && !streams.IsUnmatchedErr(err) {
 | 
						|
		// Something went wrong
 | 
						|
		return err
 | 
						|
	} else if streams.IsUnmatchedErr(err) {
 | 
						|
		// Everything went right but the callback didn't match or the ActivityStreams
 | 
						|
		// type is one that wasn't code generated.
 | 
						|
		log.Debugln("No match: ", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ResolveIRI will resolve an IRI ahd call the correct callback for the resolved type.
 | 
						|
func ResolveIRI(c context.Context, iri string, callbacks ...interface{}) error {
 | 
						|
	log.Debugln("Resolving", iri)
 | 
						|
 | 
						|
	req, _ := http.NewRequest(http.MethodGet, iri, nil)
 | 
						|
 | 
						|
	actor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
 | 
						|
	if err := crypto.SignRequest(req, nil, actor); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	response, err := http.DefaultClient.Do(req)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	defer response.Body.Close()
 | 
						|
 | 
						|
	data, err := ioutil.ReadAll(response.Body)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// fmt.Println(string(data))
 | 
						|
	return Resolve(c, data, callbacks...)
 | 
						|
}
 | 
						|
 | 
						|
// GetResolvedActorFromActorProperty resolve an external actor property to a
 | 
						|
// fully populated internal actor representation.
 | 
						|
func GetResolvedActorFromActorProperty(actor vocab.ActivityStreamsActorProperty) (apmodels.ActivityPubActor, error) {
 | 
						|
	var err error
 | 
						|
	var apActor apmodels.ActivityPubActor
 | 
						|
	resolved := false
 | 
						|
 | 
						|
	if !actor.Empty() && actor.Len() > 0 && actor.At(0) != nil {
 | 
						|
		// Explicitly use only the first actor that might be listed.
 | 
						|
		actorObjectOrIRI := actor.At(0)
 | 
						|
		var actorEntity apmodels.ExternalEntity
 | 
						|
 | 
						|
		// If the actor is an unresolved IRI then we need to resolve it.
 | 
						|
		if actorObjectOrIRI.IsIRI() {
 | 
						|
			iri := actorObjectOrIRI.GetIRI().String()
 | 
						|
			return GetResolvedActorFromIRI(iri)
 | 
						|
		}
 | 
						|
 | 
						|
		if actorObjectOrIRI.IsActivityStreamsPerson() {
 | 
						|
			actorEntity = actorObjectOrIRI.GetActivityStreamsPerson()
 | 
						|
		} else if actorObjectOrIRI.IsActivityStreamsService() {
 | 
						|
			actorEntity = actorObjectOrIRI.GetActivityStreamsService()
 | 
						|
		} else if actorObjectOrIRI.IsActivityStreamsApplication() {
 | 
						|
			actorEntity = actorObjectOrIRI.GetActivityStreamsApplication()
 | 
						|
		} else {
 | 
						|
			err = errors.New("unrecognized external ActivityPub type: " + actorObjectOrIRI.Name())
 | 
						|
			return apActor, err
 | 
						|
		}
 | 
						|
 | 
						|
		// If any of the resolution or population failed then return the error.
 | 
						|
		if err != nil {
 | 
						|
			return apActor, err
 | 
						|
		}
 | 
						|
 | 
						|
		// Convert the external AP entity into an internal actor representation.
 | 
						|
		apa, e := apmodels.MakeActorFromExernalAPEntity(actorEntity)
 | 
						|
		if apa != nil {
 | 
						|
			apActor = *apa
 | 
						|
			resolved = true
 | 
						|
		}
 | 
						|
		err = e
 | 
						|
	}
 | 
						|
 | 
						|
	if !resolved && err == nil {
 | 
						|
		err = errors.New("unknown error resolving actor from property value")
 | 
						|
	}
 | 
						|
 | 
						|
	return apActor, err
 | 
						|
}
 | 
						|
 | 
						|
// GetResolvedPublicKeyFromIRI will resolve a publicKey IRI string to a vocab.W3IDSecurityV1PublicKey.
 | 
						|
func GetResolvedPublicKeyFromIRI(publicKeyIRI string) (vocab.W3IDSecurityV1PublicKey, error) {
 | 
						|
	var err error
 | 
						|
	var pubkey vocab.W3IDSecurityV1PublicKey
 | 
						|
	resolved := false
 | 
						|
 | 
						|
	personCallback := func(c context.Context, person vocab.ActivityStreamsPerson) error {
 | 
						|
		if pkProp := person.GetW3IDSecurityV1PublicKey(); pkProp != nil {
 | 
						|
			for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
 | 
						|
				if iter.IsW3IDSecurityV1PublicKey() {
 | 
						|
					pubkey = iter.Get()
 | 
						|
					resolved = true
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return errors.New("error deriving publickey from activitystreamsperson")
 | 
						|
	}
 | 
						|
 | 
						|
	serviceCallback := func(c context.Context, service vocab.ActivityStreamsService) error {
 | 
						|
		if pkProp := service.GetW3IDSecurityV1PublicKey(); pkProp != nil {
 | 
						|
			for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
 | 
						|
				if iter.IsW3IDSecurityV1PublicKey() {
 | 
						|
					pubkey = iter.Get()
 | 
						|
					resolved = true
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return errors.New("error deriving publickey from activitystreamsservice")
 | 
						|
	}
 | 
						|
 | 
						|
	applicationCallback := func(c context.Context, app vocab.ActivityStreamsApplication) error {
 | 
						|
		if pkProp := app.GetW3IDSecurityV1PublicKey(); pkProp != nil {
 | 
						|
			for iter := pkProp.Begin(); iter != pkProp.End(); iter = iter.Next() {
 | 
						|
				if iter.IsW3IDSecurityV1PublicKey() {
 | 
						|
					pubkey = iter.Get()
 | 
						|
					resolved = true
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return errors.New("error deriving publickey from activitystreamsapp")
 | 
						|
	}
 | 
						|
 | 
						|
	pubkeyCallback := func(c context.Context, pk vocab.W3IDSecurityV1PublicKey) error {
 | 
						|
		pubkey = pk
 | 
						|
		resolved = true
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if e := ResolveIRI(context.Background(), publicKeyIRI, personCallback, serviceCallback, applicationCallback, pubkeyCallback); e != nil {
 | 
						|
		err = e
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		err = errors.Wrap(err, "error resolving publickey from iri")
 | 
						|
	}
 | 
						|
 | 
						|
	if !resolved {
 | 
						|
		err = errors.New("error resolving publickey from iri")
 | 
						|
	}
 | 
						|
 | 
						|
	return pubkey, err
 | 
						|
}
 | 
						|
 | 
						|
// GetResolvedActorFromIRI will resolve an IRI string to a fully populated actor.
 | 
						|
func GetResolvedActorFromIRI(personOrServiceIRI string) (apmodels.ActivityPubActor, error) {
 | 
						|
	var err error
 | 
						|
	var apActor apmodels.ActivityPubActor
 | 
						|
	resolved := false
 | 
						|
	personCallback := func(c context.Context, person vocab.ActivityStreamsPerson) error {
 | 
						|
		apa, e := apmodels.MakeActorFromExernalAPEntity(person)
 | 
						|
		if apa != nil {
 | 
						|
			apActor = *apa
 | 
						|
			resolved = true
 | 
						|
		}
 | 
						|
		return e
 | 
						|
	}
 | 
						|
 | 
						|
	serviceCallback := func(c context.Context, service vocab.ActivityStreamsService) error {
 | 
						|
		apa, e := apmodels.MakeActorFromExernalAPEntity(service)
 | 
						|
		if apa != nil {
 | 
						|
			apActor = *apa
 | 
						|
			resolved = true
 | 
						|
		}
 | 
						|
		return e
 | 
						|
	}
 | 
						|
 | 
						|
	applicationCallback := func(c context.Context, app vocab.ActivityStreamsApplication) error {
 | 
						|
		apa, e := apmodels.MakeActorFromExernalAPEntity(app)
 | 
						|
		if apa != nil {
 | 
						|
			apActor = *apa
 | 
						|
			resolved = true
 | 
						|
		}
 | 
						|
		return e
 | 
						|
	}
 | 
						|
 | 
						|
	if e := ResolveIRI(context.Background(), personOrServiceIRI, personCallback, serviceCallback, applicationCallback); e != nil {
 | 
						|
		err = e
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		err = errors.Wrap(err, "error resolving actor from property value")
 | 
						|
	}
 | 
						|
 | 
						|
	if !resolved {
 | 
						|
		err = errors.New("error resolving actor from property value")
 | 
						|
	}
 | 
						|
 | 
						|
	return apActor, err
 | 
						|
}
 |