Verify ActivityPub requests

This commit is contained in:
Jan-Lukas Else 2020-11-13 15:19:09 +01:00
parent 0cb142bd20
commit 9e5af4b8a7
2 changed files with 75 additions and 13 deletions

View File

@ -85,13 +85,31 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
return
}
blogIri := blog.apIri()
// Verify request
requestActor, err := apVerifySignature(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Parse activity
activity := make(map[string]interface{})
err := json.NewDecoder(r.Body).Decode(&activity)
err = json.NewDecoder(r.Body).Decode(&activity)
_ = r.Body.Close()
if err != nil {
http.Error(w, "Failed to decode body", http.StatusBadRequest)
return
}
// Get and check activity actor
activityActor, ok := activity["actor"].(string)
if !ok {
http.Error(w, "actor in activity is no string", http.StatusBadRequest)
return
}
if activityActor != requestActor.ID {
http.Error(w, "Request actor isn't activity actor", http.StatusForbidden)
return
}
// Do
switch activity["type"] {
case "Follow":
apAccept(blogName, blog, activity)
@ -99,8 +117,8 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
{
if object, ok := activity["object"].(map[string]interface{}); ok {
if objectType, ok := object["type"].(string); ok && objectType == "Follow" {
if iri, ok := object["actor"].(string); ok && iri == activity["actor"] {
_ = apRemoveFollower(blogName, iri)
if iri, ok := object["actor"].(string); ok && iri == activityActor {
_ = apRemoveFollower(blogName, activityActor)
}
}
}
@ -128,30 +146,54 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
case "Delete":
case "Block":
{
if object, ok := activity["object"].(string); ok && len(object) > 0 && activity["actor"] == object {
_ = apRemoveFollower(blogName, object)
if object, ok := activity["object"].(string); ok && len(object) > 0 && object == activityActor {
_ = apRemoveFollower(blogName, activityActor)
}
}
case "Like":
{
likeActor, likeActorOk := activity["actor"].(string)
likeObject, likeObjectOk := activity["object"].(string)
if likeActorOk && likeObjectOk && len(likeActor) > 0 && len(likeObject) > 0 && strings.Contains(likeObject, blogIri) {
sendNotification(fmt.Sprintf("%s liked %s", likeActor, likeObject))
if likeObjectOk && len(likeObject) > 0 && strings.Contains(likeObject, blogIri) {
sendNotification(fmt.Sprintf("%s liked %s", activityActor, likeObject))
}
}
case "Announce":
{
announceActor, announceActorOk := activity["actor"].(string)
announceObject, announceObjectOk := activity["object"].(string)
if announceActorOk && announceObjectOk && len(announceActor) > 0 && len(announceObject) > 0 && strings.Contains(announceObject, blogIri) {
sendNotification(fmt.Sprintf("%s announced %s", announceActor, announceObject))
if announceObjectOk && len(announceObject) > 0 && strings.Contains(announceObject, blogIri) {
sendNotification(fmt.Sprintf("%s announced %s", activityActor, announceObject))
}
}
}
// Return 201
w.WriteHeader(http.StatusCreated)
}
func apVerifySignature(r *http.Request) (*asPerson, error) {
verifier, err := httpsig.NewVerifier(r)
if err != nil {
// Error with signature header etc.
return nil, err
}
keyID := verifier.KeyId()
actor, err := apGetRemoteActor(keyID)
if err != nil {
// Actor not found or something else bad
return nil, err
}
if actor.PublicKey == nil {
return nil, errors.New("Actor has no public key")
}
block, _ := pem.Decode([]byte(actor.PublicKey.PublicKeyPem))
if block == nil {
return nil, errors.New("Public key invalid")
}
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
// Unable to parse public key
return nil, err
}
return actor, verifier.Verify(pubKey, httpsig.RSA_SHA256)
}
func handleWellKnownHostMeta(w http.ResponseWriter, r *http.Request) {
@ -222,6 +264,11 @@ func (p *post) apPost() {
createActivity["type"] = "Create"
createActivity["object"] = n
apSendToAllFollowers(p.Blog, createActivity)
if n.InReplyTo != "" {
// Is reply, so announce it
time.Sleep(30 * time.Second)
p.apAnnounce()
}
}
func (p *post) apUpdate() {
@ -233,11 +280,26 @@ func (p *post) apUpdate() {
updateActivity["@context"] = asContext
updateActivity["actor"] = appConfig.Blogs[p.Blog].apIri()
updateActivity["id"] = appConfig.Server.PublicAddress + p.Path
updateActivity["published"] = time.Now().Format("2006-01-02T15:04:05-07:00")
updateActivity["type"] = "Update"
updateActivity["object"] = n
apSendToAllFollowers(p.Blog, updateActivity)
}
func (p *post) apAnnounce() {
if !appConfig.ActivityPub.Enabled {
return
}
announceActivity := make(map[string]interface{})
announceActivity["@context"] = asContext
announceActivity["actor"] = appConfig.Blogs[p.Blog].apIri()
announceActivity["id"] = appConfig.Server.PublicAddress + p.Path + "#announce"
announceActivity["published"] = p.toASNote().Published
announceActivity["type"] = "Announce"
announceActivity["object"] = appConfig.Server.PublicAddress + p.Path
apSendToAllFollowers(p.Blog, announceActivity)
}
func (p *post) apDelete() {
if !appConfig.ActivityPub.Enabled {
return

View File

@ -88,7 +88,7 @@ func (p *post) toASNote() *asNote {
// Content
as.Content = string(p.html())
// Attachments
if images := p.Parameters["images"]; len(images) > 0 {
if images := p.Parameters[appConfig.Micropub.PhotoParam]; len(images) > 0 {
for _, image := range images {
as.Attachment = append(as.Attachment, &asAttachment{
Type: "Image",
@ -109,7 +109,7 @@ func (p *post) toASNote() *asNote {
}
}
// Reply
if replyLink := p.firstParameter("replylink"); replyLink != "" {
if replyLink := p.firstParameter(appConfig.Micropub.ReplyParam); replyLink != "" {
as.InReplyTo = replyLink
}
return as