Implement Follow-request Pending.

This commit is contained in:
Naoki Kosaka 2018-11-15 17:16:24 +09:00
parent da4eb17551
commit 747b0718c1
8 changed files with 451 additions and 126 deletions

35
RelayConf/relayconf.go Normal file
View File

@ -0,0 +1,35 @@
package relayconf
import "github.com/go-redis/redis"
// RelayConfig : struct for relay configuration
type RelayConfig struct {
BlockService bool
ManuallyAccept bool
}
// LoadConfig : Loader for relay configuration
func LoadConfig(redClient *redis.Client) RelayConfig {
blockService, err := redClient.HGet("relay:config", "block_service").Result()
if err != nil {
redClient.HSet("relay:config", "block_service", 0)
blockService = "0"
}
manuallyAccept, err := redClient.HGet("relay:config", "manually_accept").Result()
if err != nil {
redClient.HSet("relay:config", "manually_accept", 0)
manuallyAccept = "0"
}
return RelayConfig{
BlockService: blockService == "1",
ManuallyAccept: manuallyAccept == "1",
}
}
func SetConfig(redClient *redis.Client, key string, value bool) {
strValue := 0
if value {
strValue = 1
}
redClient.HSet("relay:config", key, strValue)
}

View File

@ -1,111 +1,91 @@
package main
import (
"crypto/rsa"
"fmt"
"log"
"net/url"
"os"
"strings"
machinery "github.com/RichardKnop/machinery/v1"
"github.com/RichardKnop/machinery/v1/config"
"github.com/go-redis/redis"
"github.com/urfave/cli"
"github.com/yukimochi/Activity-Relay/KeyLoader"
)
var hostname *url.URL
var hostkey *rsa.PrivateKey
var redClient *redis.Client
func listDomain(c *cli.Context) error {
var err error
var domains []string
var message string
switch c.String("type") {
case "limited":
message = " - Limited domain :"
domains, err = redClient.HKeys("relay:config:limitedDomain").Result()
if err != nil {
return err
}
case "blocked":
message = " - Blocked domain :"
domains, err = redClient.HKeys("relay:config:blockedDomain").Result()
if err != nil {
return err
}
default:
message = " - Subscribed domain :"
temp, err := redClient.Keys("relay:subscription:*").Result()
if err != nil {
return err
}
for _, domain := range temp {
domains = append(domains, strings.Replace(domain, "relay:subscription:", "", 1))
}
}
fmt.Println(message)
for _, domain := range domains {
fmt.Println(domain)
}
fmt.Println(fmt.Sprintf("Total : %d", len(domains)))
return nil
}
func manageDomain(c *cli.Context) error {
if c.String("domain") == "" {
fmt.Println("No domain given.")
return nil
}
switch c.String("type") {
case "limited":
if c.Bool("undo") {
redClient.HDel("relay:config:limitedDomain", c.String("domain"))
fmt.Println("Unregistrate [" + c.String("domain") + "] from Limited domain.")
} else {
redClient.HSet("relay:config:limitedDomain", c.String("domain"), "1")
fmt.Println("Registrate [" + c.String("domain") + "] as Limited domain.")
}
case "blocked":
if c.Bool("undo") {
redClient.HDel("relay:config:blockedDomain", c.String("domain"))
fmt.Println("Unregistrate [" + c.String("domain") + "] from Blocked domain.")
} else {
redClient.HSet("relay:config:blockedDomain", c.String("domain"), "1")
fmt.Println("Registrate [" + c.String("domain") + "] as Blocked domain.")
}
default:
fmt.Println("No type given.")
}
return nil
}
var macServer *machinery.Server
func main() {
pemPath := os.Getenv("ACTOR_PEM")
if pemPath == "" {
panic("Require ACTOR_PEM environment variable.")
}
relayDomain := os.Getenv("RELAY_DOMAIN")
if relayDomain == "" {
panic("Require RELAY_DOMAIN environment variable.")
}
redisURL := os.Getenv("REDIS_URL")
if redisURL == "" {
redisURL = "127.0.0.1:6379"
}
var err error
hostkey, err = keyloader.ReadPrivateKeyRSAfromPath(pemPath)
if err != nil {
panic("Can't read Hostkey Pemfile")
}
hostname, err = url.Parse("https://" + relayDomain)
if err != nil {
panic("Can't parse Relay Domain")
}
redClient = redis.NewClient(&redis.Options{
Addr: os.Getenv("REDIS_URL"),
Addr: redisURL,
})
var macConfig = &config.Config{
Broker: "redis://" + redisURL,
DefaultQueue: "relay",
ResultBackend: "redis://" + redisURL,
ResultsExpireIn: 5,
}
macServer, err = machinery.NewServer(macConfig)
if err != nil {
fmt.Println(err)
}
app := cli.NewApp()
app.Name = "Activity Relay Extarnal CLI"
app.Usage = "Control Relay configration"
app.Version = "0.0.2"
app.Commands = []cli.Command{
{
Name: "list-domain",
Aliases: []string{"ld"},
Name: "domain",
Usage: "Management domains",
Subcommands: []cli.Command{
{
Name: "list",
Usage: "List {subscribed,limited,blocked} domains",
Flags: []cli.Flag{
cli.StringFlag{
Name: "type, t",
Value: "subscribed",
Usage: "Registrate type [subscribed,limited,blocked]",
Usage: "Domain type [subscribed,limited,blocked]",
},
},
Action: listDomain,
Action: listDomains,
},
{
Name: "manage-domain",
Aliases: []string{"md"},
Usage: "Manage {limited,blocked} domains",
Name: "set",
Usage: "set domain type [limited,blocked]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "type, t",
Usage: "Registrate type [limited,blocked]",
Usage: "Domain type [limited,blocked]",
},
cli.StringFlag{
Name: "domain, d",
@ -116,11 +96,79 @@ func main() {
Usage: "Undo registrate",
},
},
Action: manageDomain,
Action: setDomainType,
},
},
},
{
Name: "config",
Usage: "Management relay config",
Subcommands: []cli.Command{
{
Name: "show",
Usage: "Show all relay configrations",
Action: listConfigs,
},
{
Name: "service-block",
Usage: "Enable blocking for service-type actor",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "undo, u",
Usage: "Undo block",
},
},
Action: serviceBlock,
},
{
Name: "manually-accept",
Usage: "Enable Manually accept follow-request",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "undo, u",
Usage: "Undo block",
},
},
Action: manuallyAccept,
},
},
},
{
Name: "follow-request",
Usage: "Management follow-request",
Subcommands: []cli.Command{
{
Name: "show",
Usage: "Show all follow-request",
Action: listFollows,
},
{
Name: "reject",
Usage: "Reject follow-request",
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "domain name",
},
},
Action: rejectFollow,
},
{
Name: "accept",
Usage: "Accept follow-request",
Flags: []cli.Flag{
cli.StringFlag{
Name: "domain, d",
Usage: "domain name",
},
},
Action: acceptFollow,
},
},
},
}
err := app.Run(os.Args)
err = app.Run(os.Args)
if err != nil {
log.Fatal(err)
}

35
cli/config.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"fmt"
"github.com/urfave/cli"
"github.com/yukimochi/Activity-Relay/RelayConf"
)
func serviceBlock(c *cli.Context) {
if c.Bool("undo") {
relayconf.SetConfig(redClient, "block_service", false)
fmt.Println("Blocking for service-type actor is Disabled.")
} else {
relayconf.SetConfig(redClient, "block_service", true)
fmt.Println("Blocking for service-type actor is Enabled.")
}
}
func manuallyAccept(c *cli.Context) {
if c.Bool("undo") {
relayconf.SetConfig(redClient, "manually_accept", false)
fmt.Println("Manually accept follow-request is Disabled.")
} else {
relayconf.SetConfig(redClient, "manually_accept", true)
fmt.Println("Manually accept follow-request is Enabled.")
}
}
func listConfigs(c *cli.Context) {
config := relayconf.LoadConfig(redClient)
fmt.Println("Blocking for service-type actor : ", config.BlockService)
fmt.Println("Manually accept follow-request : ", config.ManuallyAccept)
}

71
cli/domain.go Normal file
View File

@ -0,0 +1,71 @@
package main
import (
"fmt"
"strings"
"github.com/urfave/cli"
)
func listDomains(c *cli.Context) error {
var err error
var domains []string
var message string
switch c.String("type") {
case "limited":
fmt.Println(" - Limited domain :")
domains, err = redClient.HKeys("relay:config:limitedDomain").Result()
if err != nil {
return err
}
case "blocked":
fmt.Println(" - Blocked domain :")
domains, err = redClient.HKeys("relay:config:blockedDomain").Result()
if err != nil {
return err
}
default:
fmt.Println(" - Subscribed domain :")
temp, err := redClient.Keys("relay:subscription:*").Result()
if err != nil {
return err
}
for _, domain := range temp {
domains = append(domains, strings.Replace(domain, "relay:subscription:", "", 1))
}
}
fmt.Println(message)
for _, domain := range domains {
fmt.Println(domain)
}
fmt.Println(fmt.Sprintf("Total : %d", len(domains)))
return nil
}
func setDomainType(c *cli.Context) error {
if c.String("domain") == "" {
fmt.Println("No domain given.")
return nil
}
switch c.String("type") {
case "limited":
if c.Bool("undo") {
redClient.HDel("relay:config:limitedDomain", c.String("domain"))
fmt.Println("Unset [" + c.String("domain") + "] as Limited domain.")
} else {
redClient.HSet("relay:config:limitedDomain", c.String("domain"), "1")
fmt.Println("Set [" + c.String("domain") + "] as Limited domain.")
}
case "blocked":
if c.Bool("undo") {
redClient.HDel("relay:config:blockedDomain", c.String("domain"))
fmt.Println("Unset [" + c.String("domain") + "] as Blocked domain.")
} else {
redClient.HSet("relay:config:blockedDomain", c.String("domain"), "1")
fmt.Println("Set [" + c.String("domain") + "] as Blocked domain.")
}
default:
fmt.Println("No type given.")
}
return nil
}

135
cli/follow.go Normal file
View File

@ -0,0 +1,135 @@
package main
import (
"encoding/json"
"fmt"
"net/url"
"strings"
"unsafe"
"github.com/RichardKnop/machinery/v1/tasks"
"github.com/urfave/cli"
"github.com/yukimochi/Activity-Relay/ActivityPub"
)
func pushRegistorJob(inboxURL string, body []byte) {
job := &tasks.Signature{
Name: "registor",
RetryCount: 25,
Args: []tasks.Arg{
{
Name: "inboxURL",
Type: "string",
Value: inboxURL,
},
{
Name: "body",
Type: "string",
Value: *(*string)(unsafe.Pointer(&body)),
},
},
}
_, err := macServer.SendTask(job)
if err != nil {
fmt.Println(err)
}
}
func listFollows(c *cli.Context) error {
var err error
var domains []string
fmt.Println(" - Follow request :")
follows, err := redClient.Keys("relay:pending:*").Result()
if err != nil {
return err
}
for _, follow := range follows {
domains = append(domains, strings.Replace(follow, "relay:pending:", "", 1))
}
for _, domain := range domains {
fmt.Println(domain)
}
return nil
}
func acceptFollow(c *cli.Context) error {
domain := c.String("domain")
if domain != "" {
num, err := redClient.Exists("relay:pending:" + domain).Result()
if err != nil {
return err
}
if num == 0 {
fmt.Println("Given domain not found.")
return nil
}
fmt.Println("Accept Follow request : " + domain)
data, err := redClient.HGetAll("relay:pending:" + domain).Result()
if err != nil {
return err
}
activity := activitypub.Activity{
[]string{"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"},
data["activity_id"],
data["actor"],
data["type"],
data["object"],
nil,
nil,
}
actorDomain, _ := url.Parse(activity.Actor)
resp := activitypub.GenerateActivityResponse(hostname, actorDomain, "Accept", activity)
jsonData, _ := json.Marshal(&resp)
pushRegistorJob(data["inbox_url"], jsonData)
redClient.HSet("relay:subscription:"+domain, "inbox_url", data["inbox_url"])
redClient.Del("relay:pending:" + domain)
return nil
} else {
fmt.Println("No domain given.")
return nil
}
}
func rejectFollow(c *cli.Context) error {
domain := c.String("domain")
if domain != "" {
num, err := redClient.Exists("relay:pending:" + domain).Result()
if err != nil {
return err
}
if num == 0 {
fmt.Println("Given domain not found.")
return nil
}
fmt.Println("Reject Follow request : " + domain)
data, err := redClient.HGetAll("relay:pending:" + domain).Result()
if err != nil {
return err
}
activity := activitypub.Activity{
[]string{"https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"},
data["activity_id"],
data["actor"],
data["type"],
data["object"],
nil,
nil,
}
actorDomain, _ := url.Parse(activity.Actor)
resp := activitypub.GenerateActivityResponse(hostname, actorDomain, "Reject", activity)
jsonData, _ := json.Marshal(&resp)
pushRegistorJob(data["inbox_url"], jsonData)
redClient.Del("relay:pending:" + domain)
return nil
} else {
fmt.Println("No domain given.")
return nil
}
}

View File

@ -7,7 +7,9 @@ import (
)
func TestHandleInboxNoSignature(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(handleInbox))
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)
@ -22,7 +24,9 @@ func TestHandleInboxNoSignature(t *testing.T) {
}
func TestHandleInboxInvalidMethod(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(handleInbox))
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)

View File

@ -152,20 +152,21 @@ func suitableRelay(activity *activitypub.Activity, actor *activitypub.Actor) boo
if limited {
return false
}
if relConfig.blockService && actor.Type == "Service" {
if relConfig.BlockService && actor.Type == "Service" {
return false
}
return true
}
func handleInbox(w http.ResponseWriter, r *http.Request) {
func handleInbox(w http.ResponseWriter, r *http.Request, activityDecoder func(*http.Request) (*activitypub.Activity, *activitypub.Actor, []byte, error)) {
switch r.Method {
case "POST":
activity, actor, body, err := decodeActivity(r)
activity, actor, body, err := activityDecoder(r)
if err != nil {
w.WriteHeader(400)
w.Write(nil)
} else {
domain, _ := url.Parse(activity.Actor)
switch activity.Type {
case "Follow":
err = followAcceptable(activity, actor)
@ -173,29 +174,39 @@ func handleInbox(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
} else {
domain, _ := url.Parse(activity.Actor)
var responseType string
if suitableFollow(activity, actor) {
responseType = "Accept"
redClient.HSet("relay:subscription:"+domain.Host, "inbox_url", actor.Endpoints.SharedInbox)
if relConfig.ManuallyAccept {
redClient.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),
})
fmt.Println("Pending Follow Request : ", activity.Actor)
} else {
responseType = "Reject"
}
resp := activitypub.GenerateActivityResponse(hostname, domain, responseType, *activity)
resp := activitypub.GenerateActivityResponse(hostname, domain, "Accept", *activity)
jsonData, _ := json.Marshal(&resp)
go pushRegistorJob(actor.Inbox, jsonData)
redClient.HSet("relay:subscription:"+domain.Host, "inbox_url", actor.Endpoints.SharedInbox)
fmt.Println("Accept Follow Request : ", activity.Actor)
}
} else {
resp := activitypub.GenerateActivityResponse(hostname, domain, "Reject", *activity)
jsonData, _ := json.Marshal(&resp)
go pushRegistorJob(actor.Inbox, jsonData)
fmt.Println("Reject Follow Request : ", activity.Actor)
}
fmt.Println(responseType+" Follow Request : ", activity.Actor)
w.WriteHeader(202)
w.Write(nil)
}
case "Undo":
nestedActivity, _ := activitypub.DescribeNestedActivity(activity.Object)
if nestedActivity.Type == "Follow" && nestedActivity.Actor == activity.Actor {
domain, _ := url.Parse(activity.Actor)
redClient.Del("relay:subscription:" + domain.Host)
fmt.Println("Accept Unfollow Request : ", activity.Actor)
w.WriteHeader(202)
w.Write(nil)
} else {
@ -206,8 +217,8 @@ func handleInbox(w http.ResponseWriter, r *http.Request) {
} else {
domain, _ := url.Parse(activity.Actor)
go pushRelayJob(domain.Host, body)
fmt.Println("Accept Relay Status : ", activity.Actor)
w.WriteHeader(202)
w.Write(nil)
}
@ -219,9 +230,7 @@ func handleInbox(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(err.Error()))
} else {
if suitableRelay(activity, actor) {
domain, _ := url.Parse(activity.Actor)
go pushRelayJob(domain.Host, body)
fmt.Println("Accept Relay Status : ", activity.Actor)
} else {
fmt.Println("Skipping Relay Status : ", activity.Actor)

24
main.go
View File

@ -12,6 +12,7 @@ import (
"github.com/go-redis/redis"
"github.com/yukimochi/Activity-Relay/ActivityPub"
"github.com/yukimochi/Activity-Relay/KeyLoader"
"github.com/yukimochi/Activity-Relay/RelayConf"
)
// Actor : Relay's Actor
@ -20,26 +21,11 @@ var Actor activitypub.Actor
// WebfingerResource : Relay's Webfinger resource
var WebfingerResource activitypub.WebfingerResource
type relayConfig struct {
blockService bool
}
var hostname *url.URL
var hostkey *rsa.PrivateKey
var redClient *redis.Client
var macServer *machinery.Server
var relConfig relayConfig
func loadConfig() relayConfig {
blockService, err := redClient.HGet("relay:config", "block_service").Result()
if err != nil {
redClient.HSet("relay:config", "block_service", 0)
blockService = "0"
}
return relayConfig{
blockService: blockService == "1",
}
}
var relConfig relayconf.RelayConfig
func main() {
pemPath := os.Getenv("ACTOR_PEM")
@ -88,11 +74,13 @@ func main() {
WebfingerResource = activitypub.GenerateWebfingerResource(hostname, &Actor)
// Load Config
relConfig = loadConfig()
relConfig = relayconf.LoadConfig(redClient)
http.HandleFunc("/.well-known/webfinger", handleWebfinger)
http.HandleFunc("/actor", handleActor)
http.HandleFunc("/inbox", handleInbox)
http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) {
handleInbox(w, r, decodeActivity)
})
fmt.Println("Welcome to YUKIMOCHI Activity-Relay [Server]\n - Configrations")
fmt.Println("RELAY DOMAIN : ", relayDomain)