few path changes and added fix from master
This commit is contained in:
81
app/api/api.go
Normal file
81
app/api/api.go
Normal file
@ -0,0 +1,81 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/RichardKnop/machinery/v1"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
globalConfig *models.RelayConfig
|
||||
|
||||
// Actor : Relay's Actor
|
||||
Actor models.Actor
|
||||
|
||||
// WebfingerResource : Relay's Webfinger resource
|
||||
WebfingerResource models.WebfingerResource
|
||||
|
||||
// Nodeinfo : Relay's Nodeinfo
|
||||
Nodeinfo models.NodeinfoResources
|
||||
|
||||
relayState models.RelayState
|
||||
machineryServer *machinery.Server
|
||||
actorCache *cache.Cache
|
||||
)
|
||||
|
||||
func Entrypoint(g *models.RelayConfig, v string) error {
|
||||
var err error
|
||||
globalConfig = g
|
||||
version = v
|
||||
|
||||
err = initialize(globalConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registResourceHandlers()
|
||||
|
||||
logrus.Info("Staring API Server at ", globalConfig.ServerBind())
|
||||
err = http.ListenAndServe(globalConfig.ServerBind(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initialize(globalConfig *models.RelayConfig) error {
|
||||
var err error
|
||||
|
||||
redisClient := globalConfig.RedisClient()
|
||||
relayState = models.NewState(redisClient, true)
|
||||
relayState.ListenNotify(nil)
|
||||
|
||||
machineryServer, err = models.NewMachineryServer(globalConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Actor = models.NewActivityPubActorFromSelfKey(globalConfig)
|
||||
actorCache = cache.New(5*time.Minute, 10*time.Minute)
|
||||
|
||||
WebfingerResource.GenerateFromActor(globalConfig.ServerHostname(), &Actor)
|
||||
Nodeinfo.GenerateFromActor(globalConfig.ServerHostname(), &Actor, version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func registResourceHandlers() {
|
||||
http.HandleFunc("/.well-known/nodeinfo", handleNodeinfoLink)
|
||||
http.HandleFunc("/.well-known/webfinger", handleWebfinger)
|
||||
http.HandleFunc("/nodeinfo/2.1", handleNodeinfo)
|
||||
http.HandleFunc("/actor", handleActor)
|
||||
http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, decodeActivity)
|
||||
})
|
||||
}
|
39
app/api/api_test.go
Normal file
39
app/api/api_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
|
||||
testConfigPath := "../misc/test/config.yml"
|
||||
file, _ := os.Open(testConfigPath)
|
||||
defer file.Close()
|
||||
|
||||
viper.SetConfigType("yaml")
|
||||
viper.ReadConfig(file)
|
||||
viper.Set("ACTOR_PEM", "../misc/test/testKey.pem")
|
||||
viper.BindEnv("REDIS_URL")
|
||||
|
||||
globalConfig, err = models.NewRelayConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = initialize(globalConfig)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
relayState = models.NewState(relayState.RedisClient, false)
|
||||
relayState.RedisClient.FlushAll().Result()
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
}
|
70
app/api/decode.go
Normal file
70
app/api/decode.go
Normal file
@ -0,0 +1,70 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-fed/httpsig"
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
func decodeActivity(request *http.Request) (*models.Activity, *models.Actor, []byte, error) {
|
||||
request.Header.Set("Host", request.Host)
|
||||
dataLen, _ := strconv.Atoi(request.Header.Get("Content-Length"))
|
||||
body := make([]byte, dataLen)
|
||||
request.Body.Read(body)
|
||||
|
||||
// Verify HTTPSignature
|
||||
verifier, err := httpsig.NewVerifier(request)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
KeyID := verifier.KeyId()
|
||||
keyOwnerActor := new(models.Actor)
|
||||
err = keyOwnerActor.RetrieveRemoteActor(KeyID, fmt.Sprintf("%s (golang net/http; Activity-Relay %s; %s)", globalConfig.ServerServiceName(), version, globalConfig.ServerHostname().Host), actorCache)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
PubKey, err := models.ReadPublicKeyRSAFromString(keyOwnerActor.PublicKey.PublicKeyPem)
|
||||
if PubKey == nil {
|
||||
return nil, nil, nil, errors.New("Failed parse PublicKey from string")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
err = verifier.Verify(PubKey, httpsig.RSA_SHA256)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Verify Digest
|
||||
givenDigest := request.Header.Get("Digest")
|
||||
hash := sha256.New()
|
||||
hash.Write(body)
|
||||
b := hash.Sum(nil)
|
||||
calculatedDigest := "SHA-256=" + base64.StdEncoding.EncodeToString(b)
|
||||
|
||||
if givenDigest != calculatedDigest {
|
||||
return nil, nil, nil, errors.New("Digest header is mismatch")
|
||||
}
|
||||
|
||||
// Parse Activity
|
||||
var activity models.Activity
|
||||
err = json.Unmarshal(body, &activity)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var remoteActor models.Actor
|
||||
err = remoteActor.RetrieveRemoteActor(activity.Actor, fmt.Sprintf("%s (golang net/http; Activity-Relay %s; %s)", globalConfig.ServerServiceName(), version, globalConfig.ServerHostname().Host), actorCache)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return &activity, &remoteActor, body, nil
|
||||
}
|
117
app/api/decode_test.go
Normal file
117
app/api/decode_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
func TestDecodeActivity(t *testing.T) {
|
||||
relayState.RedisClient.FlushAll().Result()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "innocent.yukimochi.io",
|
||||
InboxURL: "https://innocent.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
file, _ := os.Open("../misc/test/create.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
length := strconv.Itoa(len(body))
|
||||
req, _ := http.NewRequest("POST", "/inbox", bytes.NewReader(body))
|
||||
req.Host = "relay.01.cloudgarage.yukimochi.io"
|
||||
req.Header.Add("content-length", length)
|
||||
req.Header.Add("content-type", "application/activity+json")
|
||||
req.Header.Add("date", "Sun, 23 Dec 2018 07:39:37 GMT")
|
||||
req.Header.Add("digest", "SHA-256=mxgIzbPwBuNYxmjhQeH0vWeEedQGqR1R7zMwR/XTfX8=")
|
||||
req.Header.Add("signature", `keyId="https://innocent.yukimochi.io/users/YUKIMOCHI#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="MhxXhL21RVp8VmALER2U/oJlWldJAB2COiU2QmwGopLD2pw1c32gQvg0PaBRHfMBBOsidZuRRnj43Kn488zW2xV3n3DYWcGscSh527/hhRzcpLVX2kBqbf/WeQzJmfJVuOX4SzivVhnnUB8PvlPj5LRHpw4n/ctMTq37strKDl9iZg9rej1op1YFJagDxm3iPzAhnv8lzO4RI9dstt2i/sN5EfjXai97oS7EgI//Kj1wJCRk9Pw1iTsGfPTkbk/aVZwDt7QGGvGDdO0JJjsCqtIyjojoyD9hFY9GzMqvTwVIYJrh54AUHq2i80veybaOBbCFcEaK0RpKoLs101r5Uw=="`)
|
||||
|
||||
activity, actor, _, err := decodeActivity(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
|
||||
if activity.Actor != actor.ID {
|
||||
fmt.Println(actor.ID)
|
||||
t.Fatalf("Failed - retrieved actor is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeActivityWithNoSignature(t *testing.T) {
|
||||
relayState.RedisClient.FlushAll().Result()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "innocent.yukimochi.io",
|
||||
InboxURL: "https://innocent.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
file, _ := os.Open("../misc/test/create.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
length := strconv.Itoa(len(body))
|
||||
req, _ := http.NewRequest("POST", "/inbox", bytes.NewReader(body))
|
||||
req.Host = "relay.01.cloudgarage.yukimochi.io"
|
||||
req.Header.Add("content-length", length)
|
||||
req.Header.Add("content-type", "application/activity+json")
|
||||
req.Header.Add("date", "Sun, 23 Dec 2018 07:39:37 GMT")
|
||||
req.Header.Add("digest", "SHA-256=mxgIzbPwBuNYxmjhQeH0vWeEedQGqR1R7zMwR/XTfX8=")
|
||||
|
||||
_, _, _, err := decodeActivity(req)
|
||||
if err.Error() != "neither \"Signature\" nor \"Authorization\" have signature parameters" {
|
||||
t.Fatalf("Failed - Accept request without signature")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeActivityWithNotFoundKeyId(t *testing.T) {
|
||||
relayState.RedisClient.FlushAll().Result()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "innocent.yukimochi.io",
|
||||
InboxURL: "https://innocent.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
file, _ := os.Open("../misc/test/create.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
length := strconv.Itoa(len(body))
|
||||
req, _ := http.NewRequest("POST", "/inbox", bytes.NewReader(body))
|
||||
req.Host = "relay.01.cloudgarage.yukimochi.io"
|
||||
req.Header.Add("content-length", length)
|
||||
req.Header.Add("content-type", "application/activity+json")
|
||||
req.Header.Add("date", "Sun, 23 Dec 2018 07:39:37 GMT")
|
||||
req.Header.Add("digest", "SHA-256=mxgIzbPwBuNYxmjhQeH0vWeEedQGqR1R7zMwR/XTfX8=")
|
||||
req.Header.Add("signature", `keyId="https://innocent.yukimochi.io/users/admin#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="MhxXhL21RVp8VmALER2U/oJlWldJAB2COiU2QmwGopLD2pw1c32gQvg0PaBRHfMBBOsidZuRRnj43Kn488zW2xV3n3DYWcGscSh527/hhRzcpLVX2kBqbf/WeQzJmfJVuOX4SzivVhnnUB8PvlPj5LRHpw4n/ctMTq37strKDl9iZg9rej1op1YFJagDxm3iPzAhnv8lzO4RI9dstt2i/sN5EfjXai97oS7EgI//Kj1wJCRk9Pw1iTsGfPTkbk/aVZwDt7QGGvGDdO0JJjsCqtIyjojoyD9hFY9GzMqvTwVIYJrh54AUHq2i80veybaOBbCFcEaK0RpKoLs101r5Uw=="`)
|
||||
|
||||
_, _, _, err := decodeActivity(req)
|
||||
if err.Error() != "404 Not Found" {
|
||||
t.Fatalf("Failed - Accept notfound KeyId")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeActivityWithInvalidDigest(t *testing.T) {
|
||||
relayState.RedisClient.FlushAll().Result()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "innocent.yukimochi.io",
|
||||
InboxURL: "https://innocent.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
file, _ := os.Open("../misc/test/create.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
length := strconv.Itoa(len(body))
|
||||
req, _ := http.NewRequest("POST", "/inbox", bytes.NewReader(body))
|
||||
req.Host = "relay.01.cloudgarage.yukimochi.io"
|
||||
req.Header.Add("content-length", length)
|
||||
req.Header.Add("content-type", "application/activity+json")
|
||||
req.Header.Add("date", "Sun, 23 Dec 2018 07:39:37 GMT")
|
||||
req.Header.Add("digest", "SHA-256=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
|
||||
req.Header.Add("signature", `keyId="https://innocent.yukimochi.io/users/YUKIMOCHI#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="MhxXhL21RVp8VmALER2U/oJlWldJAB2COiU2QmwGopLD2pw1c32gQvg0PaBRHfMBBOsidZuRRnj43Kn488zW2xV3n3DYWcGscSh527/hhRzcpLVX2kBqbf/WeQzJmfJVuOX4SzivVhnnUB8PvlPj5LRHpw4n/ctMTq37strKDl9iZg9rej1op1YFJagDxm3iPzAhnv8lzO4RI9dstt2i/sN5EfjXai97oS7EgI//Kj1wJCRk9Pw1iTsGfPTkbk/aVZwDt7QGGvGDdO0JJjsCqtIyjojoyD9hFY9GzMqvTwVIYJrh54AUHq2i80veybaOBbCFcEaK0RpKoLs101r5Uw=="`)
|
||||
|
||||
_, _, _, err := decodeActivity(req)
|
||||
if err.Error() != "crypto/rsa: verification error" {
|
||||
t.Fatalf("Failed - Accept unvalid digest")
|
||||
}
|
||||
}
|
323
app/api/handle.go
Normal file
323
app/api/handle.go
Normal file
@ -0,0 +1,323 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/RichardKnop/machinery/v1/tasks"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
func handleWebfinger(writer http.ResponseWriter, request *http.Request) {
|
||||
resource := request.URL.Query()["resource"]
|
||||
if request.Method != "GET" || len(resource) == 0 {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write(nil)
|
||||
} else {
|
||||
request := resource[0]
|
||||
if request == WebfingerResource.Subject {
|
||||
webfingerResource, err := json.Marshal(&WebfingerResource)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Header().Add("Content-Type", "application/json")
|
||||
writer.WriteHeader(200)
|
||||
writer.Write(webfingerResource)
|
||||
} else {
|
||||
writer.WriteHeader(404)
|
||||
writer.Write(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleNodeinfoLink(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != "GET" {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write(nil)
|
||||
} else {
|
||||
linksResource, err := json.Marshal(&Nodeinfo.NodeinfoLinks)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Header().Add("Content-Type", "application/json")
|
||||
writer.WriteHeader(200)
|
||||
writer.Write(linksResource)
|
||||
}
|
||||
}
|
||||
|
||||
func handleNodeinfo(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != "GET" {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write(nil)
|
||||
} else {
|
||||
userCount := len(relayState.Subscriptions)
|
||||
Nodeinfo.Nodeinfo.Usage.Users.Total = userCount
|
||||
Nodeinfo.Nodeinfo.Usage.Users.ActiveMonth = userCount
|
||||
Nodeinfo.Nodeinfo.Usage.Users.ActiveHalfyear = userCount
|
||||
linksResource, err := json.Marshal(&Nodeinfo.Nodeinfo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Header().Add("Content-Type", "application/json")
|
||||
writer.WriteHeader(200)
|
||||
writer.Write(linksResource)
|
||||
}
|
||||
}
|
||||
|
||||
func handleActor(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method == "GET" {
|
||||
actor, err := json.Marshal(&Actor)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
writer.Header().Add("Content-Type", "application/activity+json")
|
||||
writer.WriteHeader(200)
|
||||
writer.Write(actor)
|
||||
} else {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(entries interface{}, finder string) bool {
|
||||
switch entry := entries.(type) {
|
||||
case string:
|
||||
return entry == finder
|
||||
case []string:
|
||||
for i := 0; i < len(entry); i++ {
|
||||
if entry[i] == finder {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case []models.Subscription:
|
||||
for i := 0; i < len(entry); i++ {
|
||||
if entry[i].Domain == finder {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func pushRelayJob(sourceInbox string, body []byte) {
|
||||
for _, domain := range relayState.Subscriptions {
|
||||
if sourceInbox != domain.Domain {
|
||||
job := &tasks.Signature{
|
||||
Name: "relay",
|
||||
RetryCount: 0,
|
||||
Args: []tasks.Arg{
|
||||
{
|
||||
Name: "inboxURL",
|
||||
Type: "string",
|
||||
Value: domain.InboxURL,
|
||||
},
|
||||
{
|
||||
Name: "body",
|
||||
Type: "string",
|
||||
Value: string(body),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := machineryServer.SendTask(job)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pushRegisterJob(inboxURL string, body []byte) {
|
||||
job := &tasks.Signature{
|
||||
Name: "register",
|
||||
RetryCount: 2,
|
||||
Args: []tasks.Arg{
|
||||
{
|
||||
Name: "inboxURL",
|
||||
Type: "string",
|
||||
Value: inboxURL,
|
||||
},
|
||||
{
|
||||
Name: "body",
|
||||
Type: "string",
|
||||
Value: string(body),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := machineryServer.SendTask(job)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func followAcceptable(activity *models.Activity, actor *models.Actor) error {
|
||||
if contains(activity.Object, "https://www.w3.org/ns/activitystreams#Public") {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Follow only allowed for https://www.w3.org/ns/activitystreams#Public")
|
||||
}
|
||||
}
|
||||
|
||||
func unFollowAcceptable(activity *models.Activity, actor *models.Actor) error {
|
||||
if contains(activity.Object, "https://www.w3.org/ns/activitystreams#Public") {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Unfollow only allowed for https://www.w3.org/ns/activitystreams#Public")
|
||||
}
|
||||
}
|
||||
|
||||
func suitableFollow(activity *models.Activity, actor *models.Actor) bool {
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
if contains(relayState.BlockedDomains, domain.Host) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func relayAcceptable(activity *models.Activity, actor *models.Actor) error {
|
||||
if !contains(activity.To, "https://www.w3.org/ns/activitystreams#Public") && !contains(activity.Cc, "https://www.w3.org/ns/activitystreams#Public") {
|
||||
return errors.New("activity should contain https://www.w3.org/ns/activitystreams#Public as receiver")
|
||||
}
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
if contains(relayState.Subscriptions, domain.Host) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("to use the relay service, Subscribe me in advance")
|
||||
}
|
||||
|
||||
func suitableRelay(activity *models.Activity, actor *models.Actor) bool {
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
if contains(relayState.LimitedDomains, domain.Host) {
|
||||
return false
|
||||
}
|
||||
if relayState.RelayConfig.BlockService && actor.Type != "Person" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func handleInbox(writer http.ResponseWriter, request *http.Request, activityDecoder func(*http.Request) (*models.Activity, *models.Actor, []byte, error)) {
|
||||
switch request.Method {
|
||||
case "POST":
|
||||
activity, actor, body, err := activityDecoder(request)
|
||||
if err != nil {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write(nil)
|
||||
} else {
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
switch activity.Type {
|
||||
case "Follow":
|
||||
err = followAcceptable(activity, actor)
|
||||
if err != nil {
|
||||
resp := activity.GenerateResponse(globalConfig.ServerHostname(), "Reject")
|
||||
jsonData, _ := json.Marshal(&resp)
|
||||
go pushRegisterJob(actor.Inbox, jsonData)
|
||||
logrus.Error("Reject Follow Request : ", err.Error(), activity.Actor)
|
||||
|
||||
writer.WriteHeader(202)
|
||||
writer.Write(nil)
|
||||
} else {
|
||||
if suitableFollow(activity, actor) {
|
||||
if relayState.RelayConfig.ManuallyAccept {
|
||||
relayState.RedisClient.HMSet("relay:pending:"+domain.Host, map[string]interface{}{
|
||||
"inbox_url": actor.Endpoints.SharedInbox,
|
||||
"activity_id": activity.ID,
|
||||
"type": "Follow",
|
||||
"actor": actor.ID,
|
||||
"object": activity.Object.(string),
|
||||
})
|
||||
logrus.Info("Pending Follow Request : ", activity.Actor)
|
||||
} else {
|
||||
resp := activity.GenerateResponse(globalConfig.ServerHostname(), "Accept")
|
||||
jsonData, _ := json.Marshal(&resp)
|
||||
go pushRegisterJob(actor.Inbox, jsonData)
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: actor.Endpoints.SharedInbox,
|
||||
ActivityID: activity.ID,
|
||||
ActorID: actor.ID,
|
||||
})
|
||||
logrus.Info("Accept Follow Request : ", activity.Actor)
|
||||
}
|
||||
} else {
|
||||
resp := activity.GenerateResponse(globalConfig.ServerHostname(), "Reject")
|
||||
jsonData, _ := json.Marshal(&resp)
|
||||
go pushRegisterJob(actor.Inbox, jsonData)
|
||||
logrus.Info("Reject Follow Request : ", activity.Actor)
|
||||
}
|
||||
|
||||
writer.WriteHeader(202)
|
||||
writer.Write(nil)
|
||||
}
|
||||
case "Undo":
|
||||
nestedActivity, _ := activity.NestedActivity()
|
||||
if nestedActivity.Type == "Follow" && nestedActivity.Actor == activity.Actor {
|
||||
err = unFollowAcceptable(nestedActivity, actor)
|
||||
if err != nil {
|
||||
logrus.Error("Reject Unfollow Request : ", err.Error())
|
||||
writer.WriteHeader(400)
|
||||
writer.Write([]byte(err.Error()))
|
||||
} else {
|
||||
relayState.DelSubscription(domain.Host)
|
||||
logrus.Info("Accept Unfollow Request : ", activity.Actor)
|
||||
|
||||
writer.WriteHeader(202)
|
||||
writer.Write(nil)
|
||||
}
|
||||
} else {
|
||||
err = relayAcceptable(activity, actor)
|
||||
if err != nil {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write([]byte(err.Error()))
|
||||
} else {
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
go pushRelayJob(domain.Host, body)
|
||||
logrus.Debug("Accept Relay Status : ", activity.Actor)
|
||||
|
||||
writer.WriteHeader(202)
|
||||
writer.Write(nil)
|
||||
}
|
||||
}
|
||||
case "Create", "Update", "Delete", "Announce", "Move":
|
||||
err = relayAcceptable(activity, actor)
|
||||
if err != nil {
|
||||
writer.WriteHeader(400)
|
||||
writer.Write([]byte(err.Error()))
|
||||
} else {
|
||||
if suitableRelay(activity, actor) {
|
||||
if relayState.RelayConfig.CreateAsAnnounce && activity.Type == "Create" {
|
||||
nestedObject, err := activity.NestedActivity()
|
||||
if err != nil {
|
||||
logrus.Error("Fail Decode Activity : ", err.Error())
|
||||
}
|
||||
switch nestedObject.Type {
|
||||
case "Note":
|
||||
resp := nestedObject.GenerateAnnounce(globalConfig.ServerHostname())
|
||||
jsonData, _ := json.Marshal(&resp)
|
||||
go pushRelayJob(domain.Host, jsonData)
|
||||
logrus.Debug("Accept Announce Note : ", activity.Actor)
|
||||
default:
|
||||
logrus.Debug("Skipping Announce", nestedObject.Type, ": ", activity.Actor)
|
||||
}
|
||||
} else {
|
||||
go pushRelayJob(domain.Host, body)
|
||||
logrus.Debug("Accept Relay Status : ", activity.Actor)
|
||||
}
|
||||
} else {
|
||||
logrus.Debug("Skipping Relay Status : ", activity.Actor)
|
||||
}
|
||||
|
||||
writer.WriteHeader(202)
|
||||
writer.Write(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
writer.WriteHeader(404)
|
||||
writer.Write(nil)
|
||||
}
|
||||
}
|
786
app/api/handle_test.go
Normal file
786
app/api/handle_test.go
Normal file
@ -0,0 +1,786 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/yukimochi/Activity-Relay/models"
|
||||
)
|
||||
|
||||
const (
|
||||
BlockService models.Config = iota
|
||||
ManuallyAccept
|
||||
CreateAsAnnounce
|
||||
)
|
||||
|
||||
func TestHandleWebfingerGet(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("GET", s.URL, nil)
|
||||
q := req.URL.Query()
|
||||
q.Add("resource", "acct:relay@"+globalConfig.ServerHostname().Host)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("Failed - Content-Type not match.")
|
||||
}
|
||||
if r.StatusCode != 200 {
|
||||
t.Fatalf("Failed - StatusCode is not 200.")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
var webfingerResource models.WebfingerResource
|
||||
err = json.Unmarshal(data, &webfingerResource)
|
||||
if err != nil {
|
||||
t.Fatalf("WebfingerResource response is not valid.")
|
||||
}
|
||||
|
||||
domain, _ := url.Parse(webfingerResource.Links[0].Href)
|
||||
if domain.Host != globalConfig.ServerHostname().Host {
|
||||
t.Fatalf("WebfingerResource's Host not valid.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleWebfingerGetBadResource(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("GET", s.URL, nil)
|
||||
q := req.URL.Query()
|
||||
q.Add("resource", "acct:yukimochi@"+os.Getenv("RELAY_DOMAIN"))
|
||||
req.URL.RawQuery = q.Encode()
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 404 {
|
||||
t.Fatalf("Failed - StatusCode is not 404.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleNodeinfoLinkGet(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleNodeinfoLink))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("GET", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("Failed - Content-Type not match.")
|
||||
}
|
||||
if r.StatusCode != 200 {
|
||||
t.Fatalf("Failed - StatusCode is not 200.")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
var nodeinfoLinks models.NodeinfoLinks
|
||||
err = json.Unmarshal(data, &nodeinfoLinks)
|
||||
if err != nil {
|
||||
t.Fatalf("NodeinfoLinks response is not valid.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleNodeinfoLinkInvalidMethod(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleNodeinfoLink))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleNodeinfoGet(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleNodeinfo))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("GET", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("Failed - Content-Type not match.")
|
||||
}
|
||||
if r.StatusCode != 200 {
|
||||
t.Fatalf("Failed - StatusCode is not 200.")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
var nodeinfo models.Nodeinfo
|
||||
err = json.Unmarshal(data, &nodeinfo)
|
||||
if err != nil {
|
||||
t.Fatalf("Nodeinfo response is not valid.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleNodeinfoInvalidMethod(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleNodeinfo))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleWebfingerInvalidMethod(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleWebfinger))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleActorGet(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleActor))
|
||||
defer s.Close()
|
||||
|
||||
r, err := http.Get(s.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.Header.Get("Content-Type") != "application/activity+json" {
|
||||
t.Fatalf("Failed - Content-Type not match.")
|
||||
}
|
||||
if r.StatusCode != 200 {
|
||||
t.Fatalf("Failed - StatusCode is not 200.")
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
var actor models.Actor
|
||||
err = json.Unmarshal(data, &actor)
|
||||
if err != nil {
|
||||
t.Fatalf("Actor response is not valid.")
|
||||
}
|
||||
|
||||
domain, _ := url.Parse(actor.ID)
|
||||
if domain.Host != globalConfig.ServerHostname().Host {
|
||||
t.Fatalf("Actor's Host not valid.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleActorInvalidMethod(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(handleActor))
|
||||
defer s.Close()
|
||||
|
||||
r, err := http.Post(s.URL, "text/plain", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
data := "nil"
|
||||
sData := []string{
|
||||
"no",
|
||||
"nil",
|
||||
}
|
||||
badData := 0
|
||||
result := contains(data, "true")
|
||||
if result != false {
|
||||
t.Fatalf("Failed - no contain but true.")
|
||||
}
|
||||
result = contains(data, "nil")
|
||||
if result != true {
|
||||
t.Fatalf("Failed - contain but false.")
|
||||
}
|
||||
result = contains(sData, "true")
|
||||
if result != false {
|
||||
t.Fatalf("Failed - no contain but true. (slice)")
|
||||
}
|
||||
result = contains(sData, "nil")
|
||||
if result != true {
|
||||
t.Fatalf("Failed - contain but false. (slice)")
|
||||
}
|
||||
result = contains(badData, "hoge")
|
||||
if result != false {
|
||||
t.Fatalf("Failed - input bad data but true. (slice)")
|
||||
}
|
||||
}
|
||||
|
||||
func mockActivityDecoderProvider(activity *models.Activity, actor *models.Actor) func(r *http.Request) (*models.Activity, *models.Actor, []byte, error) {
|
||||
return func(r *http.Request) (*models.Activity, *models.Actor, []byte, error) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return activity, actor, body, nil
|
||||
}
|
||||
}
|
||||
|
||||
func mockActivity(req string) models.Activity {
|
||||
switch req {
|
||||
case "Follow":
|
||||
file, _ := os.Open("../misc/test/follow.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
case "Invalid-Follow":
|
||||
file, _ := os.Open("../misc/test/followAsActor.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
case "Unfollow":
|
||||
file, _ := os.Open("../misc/test/unfollow.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
case "Invalid-Unfollow":
|
||||
body := "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Undo\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://hacked.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Follow\",\"actor\":\"https://hacked.test.yukimochi.io/users/yukimochi\",\"object\":\"https://www.w3.org/ns/activitystreams#Public\"}}"
|
||||
var activity models.Activity
|
||||
json.Unmarshal([]byte(body), &activity)
|
||||
return activity
|
||||
case "UnfollowAsActor":
|
||||
body := "{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://mastodon.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Undo\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":{\"@context\":\"https://www.w3.org/ns/activitystreams\",\"id\":\"https://hacked.test.yukimochi.io/c125e836-e622-478e-a22d-2d9fbf2f496f\",\"type\":\"Follow\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"object\":\"https://relay.yukimochi.example.org/actor\"}}"
|
||||
var activity models.Activity
|
||||
json.Unmarshal([]byte(body), &activity)
|
||||
return activity
|
||||
case "Create":
|
||||
file, _ := os.Open("../misc/test/create.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
case "Create-Article":
|
||||
body := "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\",{\"manuallyApprovesFollowers\":\"as:manuallyApprovesFollowers\",\"sensitive\":\"as:sensitive\",\"movedTo\":{\"@id\":\"as:movedTo\",\"@type\":\"@id\"},\"Hashtag\":\"as:Hashtag\",\"ostatus\":\"http://ostatus.org#\",\"atomUri\":\"ostatus:atomUri\",\"inReplyToAtomUri\":\"ostatus:inReplyToAtomUri\",\"conversation\":\"ostatus:conversation\",\"toot\":\"http://joinmastodon.org/ns#\",\"Emoji\":\"toot:Emoji\",\"focalPoint\":{\"@container\":\"@list\",\"@id\":\"toot:focalPoint\"},\"featured\":{\"@id\":\"toot:featured\",\"@type\":\"@id\"},\"schema\":\"http://schema.org#\",\"PropertyValue\":\"schema:PropertyValue\",\"value\":\"schema:value\"}],\"id\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857/activity\",\"type\":\"Create\",\"actor\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"published\":\"2018-11-15T11:07:26Z\",\"to\":[\"https://www.w3.org/ns/activitystreams#Public\"],\"cc\":[\"https://mastodon.test.yukimochi.io/users/yukimochi/followers\"],\"object\":{\"id\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857\",\"type\":\"Article\",\"summary\":null,\"inReplyTo\":null,\"published\":\"2018-11-15T11:07:26Z\",\"url\":\"https://mastodon.test.yukimochi.io/@yukimochi/101075045564444857\",\"attributedTo\":\"https://mastodon.test.yukimochi.io/users/yukimochi\",\"to\":[\"https://www.w3.org/ns/activitystreams#Public\"],\"cc\":[\"https://mastodon.test.yukimochi.io/users/yukimochi/followers\"],\"sensitive\":false,\"atomUri\":\"https://mastodon.test.yukimochi.io/users/yukimochi/statuses/101075045564444857\",\"inReplyToAtomUri\":null,\"conversation\":\"tag:mastodon.test.yukimochi.io,2018-11-15:objectId=68:objectType=Conversation\",\"content\":\"<p>Actvity-Relay</p>\",\"contentMap\":{\"en\":\"<p>Actvity-Relay</p>\"},\"attachment\":[],\"tag\":[]},\"signature\":{\"type\":\"RsaSignature2017\",\"creator\":\"https://mastodon.test.yukimochi.io/users/yukimochi#main-key\",\"created\":\"2018-11-15T11:07:26Z\",\"signatureValue\":\"mMgl2GgVPgb1Kw6a2iDIZc7r0j3ob+Cl9y+QkCxIe6KmnUzb15e60UuhkE5j3rJnoTwRKqOFy1PMkSxlYW6fPG/5DBxW9I4kX+8sw8iH/zpwKKUOnXUJEqfwRrNH2ix33xcs/GkKPdedY6iAPV9vGZ10MSMOdypfYgU9r+UI0sTaaC2iMXH0WPnHQuYAI+Q1JDHIbDX5FH1WlDL6+8fKAicf3spBMxDwPHGPK8W2jmDLWdN2Vz4ffsCtWs5BCuqOKZrtTW0Rdd4HWzo40MnRXvBjv7yNlnnKzokANBqiOLWT7kNfK0+Vtnt6c/bNX64KBro53KR7wL3ZBvPVuv5rdQ==\"}}"
|
||||
var activity models.Activity
|
||||
json.Unmarshal([]byte(body), &activity)
|
||||
return activity
|
||||
case "Announce":
|
||||
file, _ := os.Open("../misc/test/announce.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
case "Undo":
|
||||
file, _ := os.Open("../misc/test/undo.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var activity models.Activity
|
||||
json.Unmarshal(body, &activity)
|
||||
return activity
|
||||
default:
|
||||
panic("No assigned request.")
|
||||
}
|
||||
}
|
||||
|
||||
func mockActor(req string) models.Actor {
|
||||
switch req {
|
||||
case "Person":
|
||||
file, _ := os.Open("../misc/test/person.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var actor models.Actor
|
||||
json.Unmarshal(body, &actor)
|
||||
return actor
|
||||
case "Service":
|
||||
file, _ := os.Open("../misc/test/service.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var actor models.Actor
|
||||
json.Unmarshal(body, &actor)
|
||||
return actor
|
||||
case "Application":
|
||||
file, _ := os.Open("../misc/test/application.json")
|
||||
body, _ := ioutil.ReadAll(file)
|
||||
var actor models.Actor
|
||||
json.Unmarshal(body, &actor)
|
||||
return actor
|
||||
default:
|
||||
panic("No assigned request.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuitableRelayNoBlockService(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
personActor := mockActor("Person")
|
||||
serviceActor := mockActor("Service")
|
||||
applicationActor := mockActor("Application")
|
||||
|
||||
relayState.SetConfig(BlockService, false)
|
||||
|
||||
if suitableRelay(&activity, &personActor) != true {
|
||||
t.Fatalf("Failed - Person status not relay")
|
||||
}
|
||||
if suitableRelay(&activity, &serviceActor) != true {
|
||||
t.Fatalf("Failed - Service status not relay")
|
||||
}
|
||||
if suitableRelay(&activity, &applicationActor) != true {
|
||||
t.Fatalf("Failed - Service status not relay")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuitableRelayBlockService(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
personActor := mockActor("Person")
|
||||
serviceActor := mockActor("Service")
|
||||
applicationActor := mockActor("Application")
|
||||
|
||||
relayState.SetConfig(BlockService, true)
|
||||
|
||||
if suitableRelay(&activity, &personActor) != true {
|
||||
t.Fatalf("Failed - Person status not relay")
|
||||
}
|
||||
if suitableRelay(&activity, &serviceActor) != false {
|
||||
t.Fatalf("Failed - Service status may relay when blocking mode")
|
||||
}
|
||||
if suitableRelay(&activity, &applicationActor) != false {
|
||||
t.Fatalf("Failed - Application status may relay when blocking mode")
|
||||
}
|
||||
relayState.SetConfig(BlockService, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxNoSignature(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, decodeActivity)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleInboxInvalidMethod(t *testing.T) {
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, decodeActivity)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("GET", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 404 {
|
||||
t.Fatalf("Failed - StatusCode is not 404")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleInboxValidFollow(t *testing.T) {
|
||||
activity := mockActivity("Follow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 1 {
|
||||
t.Fatalf("Failed - Subscription not works.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
}
|
||||
|
||||
func TestHandleInboxValidManuallyFollow(t *testing.T) {
|
||||
activity := mockActivity("Follow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
// Switch Manually
|
||||
relayState.SetConfig(ManuallyAccept, true)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:pending:" + domain.Host).Result()
|
||||
if res != 1 {
|
||||
t.Fatalf("Failed - Pending not works.")
|
||||
}
|
||||
res, _ = relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 0 {
|
||||
t.Fatalf("Failed - Pending was skipped.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.SetConfig(ManuallyAccept, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxInvalidFollow(t *testing.T) {
|
||||
activity := mockActivity("Invalid-Follow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.SetConfig(ManuallyAccept, false)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 0 {
|
||||
t.Fatalf("Failed - Subscription not blocked.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleInboxValidFollowBlocked(t *testing.T) {
|
||||
activity := mockActivity("Follow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.SetBlockedDomain(domain.Host, true)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 0 {
|
||||
t.Fatalf("Failed - Subscription not blocked.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.SetBlockedDomain(domain.Host, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxValidUnfollow(t *testing.T) {
|
||||
activity := mockActivity("Unfollow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 0 {
|
||||
t.Fatalf("Failed - Subscription not succeed.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
}
|
||||
|
||||
func TestHandleInboxInvalidUnfollow(t *testing.T) {
|
||||
activity := mockActivity("Invalid-Unfollow")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400")
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 1 {
|
||||
t.Fatalf("Failed - Block hacked unfollow not succeed.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
}
|
||||
|
||||
func TestHandleInboxUnfollowAsActor(t *testing.T) {
|
||||
activity := mockActivity("UnfollowAsActor")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400")
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 1 {
|
||||
t.Fatalf("Failed - Block actor unfollow not succeed.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
}
|
||||
|
||||
func TestHandleInboxValidCreate(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "example.org",
|
||||
InboxURL: "https://example.org/inbox",
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.DelSubscription("example.org")
|
||||
relayState.RedisClient.Del("relay:subscription:" + domain.Host).Result()
|
||||
relayState.RedisClient.Del("relay:subscription:example.org").Result()
|
||||
}
|
||||
|
||||
func TestHandleInboxLimitedCreate(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
relayState.SetLimitedDomain(domain.Host, true)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.SetLimitedDomain(domain.Host, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxValidCreateAsAnnounceNote(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "example.org",
|
||||
InboxURL: "https://example.org/inbox",
|
||||
})
|
||||
relayState.SetConfig(CreateAsAnnounce, true)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.DelSubscription("example.org")
|
||||
relayState.SetConfig(CreateAsAnnounce, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxValidCreateAsAnnounceNoNote(t *testing.T) {
|
||||
activity := mockActivity("Create-Article")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: "example.org",
|
||||
InboxURL: "https://example.org/inbox",
|
||||
})
|
||||
relayState.SetConfig(CreateAsAnnounce, true)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
relayState.DelSubscription("example.org")
|
||||
relayState.SetConfig(CreateAsAnnounce, false)
|
||||
}
|
||||
|
||||
func TestHandleInboxUnsubscriptionCreate(t *testing.T) {
|
||||
activity := mockActivity("Create")
|
||||
actor := mockActor("Person")
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 400 {
|
||||
t.Fatalf("Failed - StatusCode is not 400")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleInboxUndo(t *testing.T) {
|
||||
activity := mockActivity("Undo")
|
||||
actor := mockActor("Person")
|
||||
domain, _ := url.Parse(activity.Actor)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handleInbox(w, r, mockActivityDecoderProvider(&activity, &actor))
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
relayState.AddSubscription(models.Subscription{
|
||||
Domain: domain.Host,
|
||||
InboxURL: "https://mastodon.test.yukimochi.io/inbox",
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest("POST", s.URL, nil)
|
||||
client := new(http.Client)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed - " + err.Error())
|
||||
}
|
||||
if r.StatusCode != 202 {
|
||||
t.Fatalf("Failed - StatusCode is not 202 - " + strconv.Itoa(r.StatusCode))
|
||||
}
|
||||
res, _ := relayState.RedisClient.Exists("relay:subscription:" + domain.Host).Result()
|
||||
if res != 1 {
|
||||
t.Fatalf("Failed - Missing unsubscribed.")
|
||||
}
|
||||
relayState.DelSubscription(domain.Host)
|
||||
}
|
Reference in New Issue
Block a user