mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-29 22:36:14 -06:00
Merge branch 'main' into delivery_recipient_pre_sort
This commit is contained in:
commit
01adf8f307
23 changed files with 534 additions and 183 deletions
|
|
@ -24,11 +24,11 @@ In case the rate limit is exceeded, an [HTTP 429 Too Many Requests](https://deve
|
||||||
|
|
||||||
### My rate limit keeps being exceeded! Why?
|
### My rate limit keeps being exceeded! Why?
|
||||||
|
|
||||||
If you find that your rate limit is regularly being exceeded (both for yourself and other callers) during normal use of your instance, it may be that GoToSocial can't tell the clients apart by IP address. You can investigate this by viewing the logs of your instance. If (almost) all logged IP addresses appear to be the same IP address (something like `172.x.x.x`), then the rate limiting will cause problems.
|
If you find that your rate limit is regularly being exceeded (both for yourself and other callers) during normal use of your instance, it may be that GoToSocial can't tell the clients apart by IP address. You can investigate this by viewing the logs of your instance. If (almost) all logged client IP addresses appear to be the same IP address (something like `172.x.x.x`), then the rate limiting will cause problems.
|
||||||
|
|
||||||
This happens when your server is running inside NAT (port forwarding), or behind an HTTP proxy without the correct configuration, causing your instance to see all incoming IP addresses as the same address: namely, the IP address of your reverse proxy or gateway. This means that all incoming requests are *sharing the same rate limit*, rather than being split correctly per IP.
|
This happens when your server is running inside NAT (port forwarding), or behind an HTTP proxy without the correct configuration, causing your instance to see all incoming IP addresses as the same address: namely, the IP address of your reverse proxy or gateway. This means that all incoming requests are *sharing the same rate limit*, rather than being split correctly per IP.
|
||||||
|
|
||||||
If you are using an HTTP proxy then it's likely that your `trusted-proxies` is not correctly configured. If this is the case, try adding the IP address of your reverse proxy to the list of `trusted-proxies`, and restarting your instance.
|
If you are using an HTTP proxy then it's likely that your `trusted-proxies` is not correctly configured. See the [trusted-proxies](../configuration/trusted_proxies.md) documentation for more info on how to resolve this.
|
||||||
|
|
||||||
If you don't have an HTTP proxy, then it's likely caused by NAT. In this case you should disable rate limiting altogether.
|
If you don't have an HTTP proxy, then it's likely caused by NAT. In this case you should disable rate limiting altogether.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
# General
|
# General
|
||||||
|
|
||||||
The top-level configuration for GoToSocial, including basic things like host, port, bind address and transport protocol.
|
The top-level configuration for GoToSocial, including basic things like host, port, bind address, and trusted-proxies.
|
||||||
|
|
||||||
The only things you *really* need to set here are `host`, which should be the hostname where your instance is reachable, and probably `port`.
|
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
|
|
|
||||||
71
docs/configuration/trusted_proxies.md
Normal file
71
docs/configuration/trusted_proxies.md
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Trusted Proxies
|
||||||
|
|
||||||
|
To correctly enforce [rate limiting](../api/ratelimiting.md), GoToSocial relies on the concept of "trusted proxies" in order to accurately determine the IP address of clients accessing your server.
|
||||||
|
|
||||||
|
A "trusted proxy" is an intermediate network hop that GoToSocial can be instructed to trust to provide a correct client IP address.
|
||||||
|
|
||||||
|
For example, if you are running in a reverse proxy configuration with Docker + Nginx, then the Docker network address of Nginx should be configured as a trusted proxy, since all traffic from the wider internet will come into GoToSocial via Nginx.
|
||||||
|
|
||||||
|
Without setting `trusted-proxies` correctly, GoToSocial will see all incoming client IP addresses as the same address, which leads to rate limiting issues, since GoToSocial uses client IP addresses to bucket rate limits.
|
||||||
|
|
||||||
|
## tl;dr: How to set `trusted-proxies` correctly
|
||||||
|
|
||||||
|
If your `trusted-proxies` setting is not correctly configured, you may see the following warning on the web view of your instance (v0.18.0 and above):
|
||||||
|
|
||||||
|
> Warning! It looks like trusted-proxies is not set correctly in this instance's configuration. This may cause rate-limiting issues and, by extension, federation issues.
|
||||||
|
>
|
||||||
|
> If you are the instance admin, you should fix this by adding `SUGGESTED_IP_RANGE` to your trusted-proxies.
|
||||||
|
|
||||||
|
To resolve this, copy the IP range in the message, and edit your `config.yaml` file to add the IP range to your `trusted-proxies`.
|
||||||
|
|
||||||
|
!!! tip "You may be getting rate limited even if you don't see the above warning!"
|
||||||
|
If you're on a version of GoToSocial below v0.18.0, or you're running behind a CDN such as Cloudflare (not recommended), you won't see a warning message. Instead, you'll see in your GoToSocial logs that all client IPs are the same address. In this case, take the recurring client IP value as `SUGGESTED_IP_RANGE`.
|
||||||
|
|
||||||
|
In this example, we assume `SUGGESTED_IP_RANGE` to be `172.17.0.1/16` (the default Docker bridge network subnet).
|
||||||
|
|
||||||
|
Before (default config):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
trusted-proxies:
|
||||||
|
- "127.0.0.1/32"
|
||||||
|
- "::1"
|
||||||
|
```
|
||||||
|
|
||||||
|
After (new config):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
trusted-proxies:
|
||||||
|
- "172.17.0.1/16"
|
||||||
|
- "127.0.0.1/32"
|
||||||
|
- "::1"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using [environment variables](../configuration/index.md#environment-variables) to configure your instance, you can configure `trusted-proxies` by setting the environment variable `GTS_TRUSTED_PROXIES` to a comma-separated list of IP ranges, like so:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GTS_TRUSTED_PROXIES="172.17.0.1/16,127.0.0.1/32,::1"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using docker compose, your docker-compose.yaml file should look something like this after the change (note that yaml uses `: ` and not `=`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
################################
|
||||||
|
# BLAH BLAH OTHER CONFIG STUFF #
|
||||||
|
################################
|
||||||
|
environment:
|
||||||
|
############################
|
||||||
|
# BLAH BLAH OTHER ENV VARS #
|
||||||
|
############################
|
||||||
|
## For reverse proxy setups:
|
||||||
|
GTS_TRUSTED_PROXIES: "172.17.0.1/16,127.0.0.1/32,::1"
|
||||||
|
################################
|
||||||
|
# BLAH BLAH OTHER CONFIG STUFF #
|
||||||
|
################################
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have made the necessary configuration changes, restart your instance and refresh the home page. If the message is gone, then the problem is resolved!
|
||||||
|
|
||||||
|
If you still see the warning message but with a different suggested IP range to add to `trusted-proxies`, then follow the same steps as above again, including the new suggested IP range in your config in addition to the one you just added.
|
||||||
|
|
||||||
|
!!! tip "Cloudflare IP Addresses"
|
||||||
|
If you are running with a CDN/proxy such as Cloudflare in front of your GoToSocial instance (not recommended), then you may need to add one or more of the Cloudflare IP addresses to your `trusted-proxies` in order to have rate limiting work properly. You can find a list of Cloudflare IP addresses here: https://www.cloudflare.com/ips/
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
# Actors and Actor Properties
|
# Actors and Actor Properties
|
||||||
|
|
||||||
|
## `Service` vs `Person` actors
|
||||||
|
|
||||||
|
GoToSocial serves most accounts as the ActivityStreams `Person` type described [here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person).
|
||||||
|
|
||||||
|
Accounts that users have selected to mark as bot accounts, however, will use the `Service` type described [here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service).
|
||||||
|
|
||||||
|
This type distinction can be used by remote servers to distinguish between bot accounts and "regular" user accounts.
|
||||||
|
|
||||||
## Inbox
|
## Inbox
|
||||||
|
|
||||||
GoToSocial implements Inboxes for Actors following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#inbox).
|
GoToSocial implements Inboxes for Actors following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#inbox).
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,7 @@ We have guides available for the following servers:
|
||||||
When using a reverse-proxy, special care must be taken to allow WebSockets to work too. This is necessary as many client applications use WebSockets to stream your timeline. WebSockets is not used as part of federation.
|
When using a reverse-proxy, special care must be taken to allow WebSockets to work too. This is necessary as many client applications use WebSockets to stream your timeline. WebSockets is not used as part of federation.
|
||||||
|
|
||||||
Make sure you read the [WebSocket](websocket.md) documentation and configure your reverse proxy accordingly.
|
Make sure you read the [WebSocket](websocket.md) documentation and configure your reverse proxy accordingly.
|
||||||
|
|
||||||
|
## Trusted Proxies
|
||||||
|
|
||||||
|
When using a reverse-proxy, you may run into issues with rate limiting and `trusted-proxies`. Check the [trusted proxies](../../configuration/trusted_proxies.md) documentation if you have any problems.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
version: "3.3"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
gotosocial:
|
gotosocial:
|
||||||
image: superseriousbusiness/gotosocial:latest
|
image: superseriousbusiness/gotosocial:latest
|
||||||
|
|
@ -24,7 +22,7 @@ services:
|
||||||
# Wazero compilation cache will be stored.
|
# Wazero compilation cache will be stored.
|
||||||
GTS_WAZERO_COMPILATION_CACHE: /gotosocial/.cache
|
GTS_WAZERO_COMPILATION_CACHE: /gotosocial/.cache
|
||||||
## For reverse proxy setups:
|
## For reverse proxy setups:
|
||||||
# GTS_TRUSTED_PROXIES: "172.x.x.x"
|
GTS_TRUSTED_PROXIES: "172.18.0.1/16"
|
||||||
## Set the timezone of your server:
|
## Set the timezone of your server:
|
||||||
#TZ: UTC
|
#TZ: UTC
|
||||||
ports:
|
ports:
|
||||||
|
|
@ -47,3 +45,6 @@ networks:
|
||||||
gotosocial:
|
gotosocial:
|
||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
|
config:
|
||||||
|
- subnet: "172.18.0.0/16"
|
||||||
|
gateway: "172.18.0.1"
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,22 @@
|
||||||
|
|
||||||
package ap
|
package ap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/activity/pub"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicURI returns a fresh copy of the *url.URL version of the
|
||||||
|
// magic ActivityPub URI https://www.w3.org/ns/activitystreams#Public
|
||||||
|
func PublicURI() *url.URL {
|
||||||
|
publicURI, err := url.Parse(pub.PublicActivityPubIRI)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return publicURI
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/activitystreams-vocabulary
|
// https://www.w3.org/TR/activitystreams-vocabulary
|
||||||
const (
|
const (
|
||||||
ActivityAccept = "Accept" // ActivityStreamsAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept
|
ActivityAccept = "Accept" // ActivityStreamsAccept https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/pub"
|
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
|
|
@ -111,7 +110,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
||||||
|
|
||||||
// Anyone can like.
|
// Anyone can like.
|
||||||
canLikeAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
canLikeAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||||
canLikeAlwaysProp.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
canLikeAlwaysProp.AppendIRI(ap.PublicURI())
|
||||||
canLike.SetGoToSocialAlways(canLikeAlwaysProp)
|
canLike.SetGoToSocialAlways(canLikeAlwaysProp)
|
||||||
|
|
||||||
// Empty approvalRequired.
|
// Empty approvalRequired.
|
||||||
|
|
@ -128,7 +127,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
||||||
|
|
||||||
// Anyone can reply.
|
// Anyone can reply.
|
||||||
canReplyAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
canReplyAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||||
canReplyAlwaysProp.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
canReplyAlwaysProp.AppendIRI(ap.PublicURI())
|
||||||
canReply.SetGoToSocialAlways(canReplyAlwaysProp)
|
canReply.SetGoToSocialAlways(canReplyAlwaysProp)
|
||||||
|
|
||||||
// Set empty approvalRequired.
|
// Set empty approvalRequired.
|
||||||
|
|
@ -151,7 +150,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
||||||
|
|
||||||
// Public requires approval to announce.
|
// Public requires approval to announce.
|
||||||
canAnnounceApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
canAnnounceApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
||||||
canAnnounceApprovalRequiredProp.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
canAnnounceApprovalRequiredProp.AppendIRI(ap.PublicURI())
|
||||||
canAnnounce.SetGoToSocialApprovalRequired(canAnnounceApprovalRequiredProp)
|
canAnnounce.SetGoToSocialApprovalRequired(canAnnounceApprovalRequiredProp)
|
||||||
|
|
||||||
// Set canAnnounce on the policy.
|
// Set canAnnounce on the policy.
|
||||||
|
|
@ -266,7 +265,7 @@ func addressable1() ap.Addressable {
|
||||||
note := streams.NewActivityStreamsNote()
|
note := streams.NewActivityStreamsNote()
|
||||||
|
|
||||||
toProp := streams.NewActivityStreamsToProperty()
|
toProp := streams.NewActivityStreamsToProperty()
|
||||||
toProp.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
toProp.AppendIRI(ap.PublicURI())
|
||||||
|
|
||||||
note.SetActivityStreamsTo(toProp)
|
note.SetActivityStreamsTo(toProp)
|
||||||
|
|
||||||
|
|
@ -288,7 +287,7 @@ func addressable2() ap.Addressable {
|
||||||
note.SetActivityStreamsTo(toProp)
|
note.SetActivityStreamsTo(toProp)
|
||||||
|
|
||||||
ccProp := streams.NewActivityStreamsCcProperty()
|
ccProp := streams.NewActivityStreamsCcProperty()
|
||||||
ccProp.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
ccProp.AppendIRI(ap.PublicURI())
|
||||||
|
|
||||||
note.SetActivityStreamsCc(ccProp)
|
note.SetActivityStreamsCc(ccProp)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ type Accountable interface {
|
||||||
WithTag
|
WithTag
|
||||||
WithPublished
|
WithPublished
|
||||||
WithUpdated
|
WithUpdated
|
||||||
|
WithImage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Statusable represents the minimum activitypub interface for representing a 'status'.
|
// Statusable represents the minimum activitypub interface for representing a 'status'.
|
||||||
|
|
@ -439,6 +440,7 @@ type WithValue interface {
|
||||||
// WithImage represents an activity with ActivityStreamsImageProperty
|
// WithImage represents an activity with ActivityStreamsImageProperty
|
||||||
type WithImage interface {
|
type WithImage interface {
|
||||||
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
||||||
|
SetActivityStreamsImage(vocab.ActivityStreamsImageProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSummary represents an activity with ActivityStreamsSummaryProperty
|
// WithSummary represents an activity with ActivityStreamsSummaryProperty
|
||||||
|
|
|
||||||
|
|
@ -177,38 +177,6 @@ func (suite *InboxPostTestSuite) newUndo(
|
||||||
return undo
|
return undo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *InboxPostTestSuite) newUpdatePerson(person vocab.ActivityStreamsPerson, cc string, updateIRI string) vocab.ActivityStreamsUpdate {
|
|
||||||
// create an update
|
|
||||||
update := streams.NewActivityStreamsUpdate()
|
|
||||||
|
|
||||||
// set the appropriate actor on it
|
|
||||||
updateActor := streams.NewActivityStreamsActorProperty()
|
|
||||||
updateActor.AppendIRI(person.GetJSONLDId().Get())
|
|
||||||
update.SetActivityStreamsActor(updateActor)
|
|
||||||
|
|
||||||
// Set the person as the 'object' property.
|
|
||||||
updateObject := streams.NewActivityStreamsObjectProperty()
|
|
||||||
updateObject.AppendActivityStreamsPerson(person)
|
|
||||||
update.SetActivityStreamsObject(updateObject)
|
|
||||||
|
|
||||||
// Set the To of the update as public
|
|
||||||
updateTo := streams.NewActivityStreamsToProperty()
|
|
||||||
updateTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
|
||||||
update.SetActivityStreamsTo(updateTo)
|
|
||||||
|
|
||||||
// set the cc of the update to the receivingAccount
|
|
||||||
updateCC := streams.NewActivityStreamsCcProperty()
|
|
||||||
updateCC.AppendIRI(testrig.URLMustParse(cc))
|
|
||||||
update.SetActivityStreamsCc(updateCC)
|
|
||||||
|
|
||||||
// set some random-ass ID for the activity
|
|
||||||
updateID := streams.NewJSONLDIdProperty()
|
|
||||||
updateID.SetIRI(testrig.URLMustParse(updateIRI))
|
|
||||||
update.SetJSONLDId(updateID)
|
|
||||||
|
|
||||||
return update
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, deleteIRI string) vocab.ActivityStreamsDelete {
|
func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, deleteIRI string) vocab.ActivityStreamsDelete {
|
||||||
// create a delete
|
// create a delete
|
||||||
delete := streams.NewActivityStreamsDelete()
|
delete := streams.NewActivityStreamsDelete()
|
||||||
|
|
@ -225,7 +193,7 @@ func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, de
|
||||||
|
|
||||||
// Set the To of the delete as public
|
// Set the To of the delete as public
|
||||||
deleteTo := streams.NewActivityStreamsToProperty()
|
deleteTo := streams.NewActivityStreamsToProperty()
|
||||||
deleteTo.AppendIRI(testrig.URLMustParse(pub.PublicActivityPubIRI))
|
deleteTo.AppendIRI(ap.PublicURI())
|
||||||
delete.SetActivityStreamsTo(deleteTo)
|
delete.SetActivityStreamsTo(deleteTo)
|
||||||
|
|
||||||
// set some random-ass ID for the activity
|
// set some random-ass ID for the activity
|
||||||
|
|
@ -329,7 +297,6 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
var (
|
var (
|
||||||
requestingAccount = new(gtsmodel.Account)
|
requestingAccount = new(gtsmodel.Account)
|
||||||
targetAccount = suite.testAccounts["local_account_1"]
|
targetAccount = suite.testAccounts["local_account_1"]
|
||||||
activityID = "http://fossbros-anonymous.io/72cc96a3-f742-4daf-b9f5-3407667260c5"
|
|
||||||
updatedDisplayName = "updated display name!"
|
updatedDisplayName = "updated display name!"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -348,11 +315,19 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
requestingAccount.Emojis = []*gtsmodel.Emoji{testEmoji}
|
requestingAccount.Emojis = []*gtsmodel.Emoji{testEmoji}
|
||||||
|
|
||||||
// Create an update from the account.
|
// Create an update from the account.
|
||||||
asAccount, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
accountable, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
update := suite.newUpdatePerson(asAccount, targetAccount.URI, activityID)
|
update, err := suite.tc.WrapAccountableInUpdate(accountable)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the ID to something from fossbros anonymous.
|
||||||
|
idProp := streams.NewJSONLDIdProperty()
|
||||||
|
idProp.SetIRI(testrig.URLMustParse("https://fossbros-anonymous.io/updates/waaaaaaaaaaaaaaaaa"))
|
||||||
|
update.SetJSONLDId(idProp)
|
||||||
|
|
||||||
// Update.
|
// Update.
|
||||||
suite.inboxPost(
|
suite.inboxPost(
|
||||||
|
|
@ -540,17 +515,20 @@ func (suite *InboxPostTestSuite) TestPostFromBlockedAccount() {
|
||||||
var (
|
var (
|
||||||
requestingAccount = suite.testAccounts["remote_account_1"]
|
requestingAccount = suite.testAccounts["remote_account_1"]
|
||||||
targetAccount = suite.testAccounts["local_account_2"]
|
targetAccount = suite.testAccounts["local_account_2"]
|
||||||
activityID = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
person, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
// Create an update from the account.
|
||||||
|
accountable, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
update, err := suite.tc.WrapAccountableInUpdate(accountable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post an update from foss satan to turtle, who blocks him.
|
// Post an update from foss satan
|
||||||
update := suite.newUpdatePerson(person, targetAccount.URI, activityID)
|
// to turtle, who blocks him.
|
||||||
|
|
||||||
suite.inboxPost(
|
suite.inboxPost(
|
||||||
update,
|
update,
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
@ -63,6 +64,11 @@ type WebPage struct {
|
||||||
// ogMeta, stylesheets, javascript, and any extra
|
// ogMeta, stylesheets, javascript, and any extra
|
||||||
// properties will be provided to the template if
|
// properties will be provided to the template if
|
||||||
// set, but can all be nil.
|
// set, but can all be nil.
|
||||||
|
//
|
||||||
|
// TemplateWebPage also checks whether the requesting
|
||||||
|
// clientIP is 127.0.0.1 or within a private IP range.
|
||||||
|
// If so, it injects a suggestion into the page header
|
||||||
|
// about setting trusted-proxies correctly.
|
||||||
func TemplateWebPage(
|
func TemplateWebPage(
|
||||||
c *gin.Context,
|
c *gin.Context,
|
||||||
page WebPage,
|
page WebPage,
|
||||||
|
|
@ -74,13 +80,86 @@ func TemplateWebPage(
|
||||||
"javascript": page.Javascript,
|
"javascript": page.Javascript,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add extras to template object.
|
||||||
for k, v := range page.Extra {
|
for k, v := range page.Extra {
|
||||||
obj[k] = v
|
obj[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject trustedProxiesRec to template
|
||||||
|
// object (or noop if not necessary).
|
||||||
|
injectTrustedProxiesRec(c, obj)
|
||||||
|
|
||||||
templatePage(c, page.Template, http.StatusOK, obj)
|
templatePage(c, page.Template, http.StatusOK, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func injectTrustedProxiesRec(
|
||||||
|
c *gin.Context,
|
||||||
|
obj map[string]any,
|
||||||
|
) {
|
||||||
|
clientIP := c.ClientIP()
|
||||||
|
if clientIP == "127.0.0.1" {
|
||||||
|
// Suggest precise 127.0.0.1/32.
|
||||||
|
trustedProxiesRec := clientIP + "/32"
|
||||||
|
obj["trustedProxiesRec"] = trustedProxiesRec
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if "X-Forwarded-For"
|
||||||
|
// or "X-Real-IP" were set.
|
||||||
|
var hasRemoteIPHeader bool
|
||||||
|
for _, k := range []string{
|
||||||
|
"X-Forwarded-For",
|
||||||
|
"X-Real-IP",
|
||||||
|
} {
|
||||||
|
if v := c.GetHeader(k); v != "" {
|
||||||
|
hasRemoteIPHeader = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasRemoteIPHeader {
|
||||||
|
// Upstream hasn't set a
|
||||||
|
// remote IP header, bail.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(clientIP)
|
||||||
|
if !ip.IsPrivate() {
|
||||||
|
// Upstream set a remote IP
|
||||||
|
// header but final clientIP
|
||||||
|
// isn't private, so upstream
|
||||||
|
// is probably already trusted.
|
||||||
|
// Don't inject suggestion.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private IP, guess if Docker.
|
||||||
|
if dockerSubnet.Contains(ip) {
|
||||||
|
// Suggest a CIDR that likely
|
||||||
|
// covers this Docker subnet,
|
||||||
|
// eg., 172.17.0.0 -> 172.17.255.255.
|
||||||
|
trustedProxiesRec := clientIP + "/16"
|
||||||
|
obj["trustedProxiesRec"] = trustedProxiesRec
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private IP but we don't know
|
||||||
|
// what it is. Suggest precise CIDR.
|
||||||
|
trustedProxiesRec := clientIP + "/32"
|
||||||
|
obj["trustedProxiesRec"] = trustedProxiesRec
|
||||||
|
}
|
||||||
|
|
||||||
|
// dockerSubnet is a CIDR that lets one make hazy guesses
|
||||||
|
// as to whether an address is within the ranges Docker
|
||||||
|
// uses for subnets, ie., 172.16.0.0 -> 172.31.255.255.
|
||||||
|
var dockerSubnet = func() *net.IPNet {
|
||||||
|
_, subnet, err := net.ParseCIDR("172.16.0.0/12")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return subnet
|
||||||
|
}()
|
||||||
|
|
||||||
// templateErrorPage renders the given
|
// templateErrorPage renders the given
|
||||||
// HTTP code, error, and request ID
|
// HTTP code, error, and request ID
|
||||||
// within the standard error template.
|
// within the standard error template.
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,12 @@ func (suite *FederatorStandardTestSuite) SetupTest() {
|
||||||
|
|
||||||
// Ensure it's possible to deref
|
// Ensure it's possible to deref
|
||||||
// main key of foss satan.
|
// main key of foss satan.
|
||||||
fossSatanPerson, err := suite.typeconverter.AccountToAS(context.Background(), suite.testAccounts["remote_account_1"])
|
fossSatanAS, err := suite.typeconverter.AccountToAS(context.Background(), suite.testAccounts["remote_account_1"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.httpClient = testrig.NewMockHTTPClient(nil, "../../testrig/media", fossSatanPerson)
|
suite.httpClient = testrig.NewMockHTTPClient(nil, "../../testrig/media", fossSatanAS)
|
||||||
suite.httpClient.TestRemotePeople = testrig.NewTestFediPeople()
|
suite.httpClient.TestRemotePeople = testrig.NewTestFediPeople()
|
||||||
suite.httpClient.TestRemoteStatuses = testrig.NewTestFediStatuses()
|
suite.httpClient.TestRemoteStatuses = testrig.NewTestFediStatuses()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
|
@ -72,7 +71,7 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth passed, generate the proper AP representation.
|
// Auth passed, generate the proper AP representation.
|
||||||
person, err := p.converter.AccountToAS(ctx, receiver)
|
accountable, err := p.converter.AccountToAS(ctx, receiver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := gtserror.Newf("error converting account: %w", err)
|
err := gtserror.Newf("error converting account: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
|
@ -91,7 +90,7 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
||||||
// Instead, we end up in an 'I'll show you mine if you show me
|
// Instead, we end up in an 'I'll show you mine if you show me
|
||||||
// yours' situation, where we sort of agree to reveal each
|
// yours' situation, where we sort of agree to reveal each
|
||||||
// other's profiles at the same time.
|
// other's profiles at the same time.
|
||||||
return data(person)
|
return data(accountable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get requester from auth.
|
// Get requester from auth.
|
||||||
|
|
@ -107,13 +106,13 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
||||||
return nil, gtserror.NewErrorForbidden(errors.New(text))
|
return nil, gtserror.NewErrorForbidden(errors.New(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
return data(person)
|
return data(accountable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func data(requestedPerson vocab.ActivityStreamsPerson) (interface{}, gtserror.WithCode) {
|
func data(accountable ap.Accountable) (interface{}, gtserror.WithCode) {
|
||||||
data, err := ap.Serialize(requestedPerson)
|
data, err := ap.Serialize(accountable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := gtserror.Newf("error serializing person: %w", err)
|
err := gtserror.Newf("error serializing accountable: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/activity/pub"
|
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
|
|
@ -93,11 +92,6 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
publicIRI, err := parseURI(pub.PublicActivityPubIRI)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new delete.
|
// Create a new delete.
|
||||||
// todo: tc.AccountToASDelete
|
// todo: tc.AccountToASDelete
|
||||||
delete := streams.NewActivityStreamsDelete()
|
delete := streams.NewActivityStreamsDelete()
|
||||||
|
|
@ -121,7 +115,7 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
|
||||||
|
|
||||||
// Address the delete CC public.
|
// Address the delete CC public.
|
||||||
deleteCC := streams.NewActivityStreamsCcProperty()
|
deleteCC := streams.NewActivityStreamsCcProperty()
|
||||||
deleteCC.AppendIRI(publicIRI)
|
deleteCC.AppendIRI(ap.PublicURI())
|
||||||
delete.SetActivityStreamsCc(deleteCC)
|
delete.SetActivityStreamsCc(deleteCC)
|
||||||
|
|
||||||
// Send the Delete via the Actor's outbox.
|
// Send the Delete via the Actor's outbox.
|
||||||
|
|
@ -877,14 +871,14 @@ func (f *federate) UpdateAccount(ctx context.Context, account *gtsmodel.Account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert account to ActivityStreams Person.
|
// Convert account to Accountable.
|
||||||
person, err := f.converter.AccountToAS(ctx, account)
|
accountable, err := f.converter.AccountToAS(ctx, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gtserror.Newf("error converting account to Person: %w", err)
|
return gtserror.Newf("error converting account to Person: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use ActivityStreams Person as Object of Update.
|
// Use Accountable as Object of Update.
|
||||||
update, err := f.converter.WrapPersonInUpdate(person, account)
|
update, err := f.converter.WrapAccountableInUpdate(accountable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gtserror.Newf("error wrapping Person in Update: %w", err)
|
return gtserror.Newf("error wrapping Person in Update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1089,11 +1083,6 @@ func (f *federate) MoveAccount(ctx context.Context, account *gtsmodel.Account) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
publicIRI, err := parseURI(pub.PublicActivityPubIRI)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new move.
|
// Create a new move.
|
||||||
move := streams.NewActivityStreamsMove()
|
move := streams.NewActivityStreamsMove()
|
||||||
|
|
||||||
|
|
@ -1115,7 +1104,7 @@ func (f *federate) MoveAccount(ctx context.Context, account *gtsmodel.Account) e
|
||||||
ap.AppendTo(move, followersIRI)
|
ap.AppendTo(move, followersIRI)
|
||||||
|
|
||||||
// Address the move CC public.
|
// Address the move CC public.
|
||||||
ap.AppendCc(move, publicIRI)
|
ap.AppendCc(move, ap.PublicURI())
|
||||||
|
|
||||||
// Send the Move via the Actor's outbox.
|
// Send the Move via the Actor's outbox.
|
||||||
if _, err := f.FederatingActor().Send(
|
if _, err := f.FederatingActor().Send(
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,24 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountToAS converts a gts model account into an activity streams person, suitable for federation
|
// AccountToAS converts a gts model account
|
||||||
func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
|
// into an activity streams person or service.
|
||||||
person := streams.NewActivityStreamsPerson()
|
func (c *Converter) AccountToAS(
|
||||||
|
ctx context.Context,
|
||||||
|
a *gtsmodel.Account,
|
||||||
|
) (ap.Accountable, error) {
|
||||||
|
// accountable is a service if this
|
||||||
|
// is a bot account, otherwise a person.
|
||||||
|
var accountable ap.Accountable
|
||||||
|
if util.PtrOrZero(a.Bot) {
|
||||||
|
accountable = streams.NewActivityStreamsService()
|
||||||
|
} else {
|
||||||
|
accountable = streams.NewActivityStreamsPerson()
|
||||||
|
}
|
||||||
|
|
||||||
// id should be the activitypub URI of this user
|
// id should be the activitypub URI of this user
|
||||||
// something like https://example.org/users/example_user
|
// something like https://example.org/users/example_user
|
||||||
|
|
@ -51,7 +63,13 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
idProp := streams.NewJSONLDIdProperty()
|
idProp := streams.NewJSONLDIdProperty()
|
||||||
idProp.SetIRI(profileIDURI)
|
idProp.SetIRI(profileIDURI)
|
||||||
person.SetJSONLDId(idProp)
|
accountable.SetJSONLDId(idProp)
|
||||||
|
|
||||||
|
// published
|
||||||
|
// The moment when the account was created.
|
||||||
|
publishedProp := streams.NewActivityStreamsPublishedProperty()
|
||||||
|
publishedProp.Set(a.CreatedAt)
|
||||||
|
accountable.SetActivityStreamsPublished(publishedProp)
|
||||||
|
|
||||||
// following
|
// following
|
||||||
// The URI for retrieving a list of accounts this user is following
|
// The URI for retrieving a list of accounts this user is following
|
||||||
|
|
@ -61,7 +79,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
followingProp := streams.NewActivityStreamsFollowingProperty()
|
followingProp := streams.NewActivityStreamsFollowingProperty()
|
||||||
followingProp.SetIRI(followingURI)
|
followingProp.SetIRI(followingURI)
|
||||||
person.SetActivityStreamsFollowing(followingProp)
|
accountable.SetActivityStreamsFollowing(followingProp)
|
||||||
|
|
||||||
// followers
|
// followers
|
||||||
// The URI for retrieving a list of this user's followers
|
// The URI for retrieving a list of this user's followers
|
||||||
|
|
@ -71,7 +89,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
followersProp := streams.NewActivityStreamsFollowersProperty()
|
followersProp := streams.NewActivityStreamsFollowersProperty()
|
||||||
followersProp.SetIRI(followersURI)
|
followersProp.SetIRI(followersURI)
|
||||||
person.SetActivityStreamsFollowers(followersProp)
|
accountable.SetActivityStreamsFollowers(followersProp)
|
||||||
|
|
||||||
// inbox
|
// inbox
|
||||||
// the activitypub inbox of this user for accepting messages
|
// the activitypub inbox of this user for accepting messages
|
||||||
|
|
@ -81,7 +99,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
inboxProp := streams.NewActivityStreamsInboxProperty()
|
inboxProp := streams.NewActivityStreamsInboxProperty()
|
||||||
inboxProp.SetIRI(inboxURI)
|
inboxProp.SetIRI(inboxURI)
|
||||||
person.SetActivityStreamsInbox(inboxProp)
|
accountable.SetActivityStreamsInbox(inboxProp)
|
||||||
|
|
||||||
// shared inbox -- only add this if we know for sure it has one
|
// shared inbox -- only add this if we know for sure it has one
|
||||||
if a.SharedInboxURI != nil && *a.SharedInboxURI != "" {
|
if a.SharedInboxURI != nil && *a.SharedInboxURI != "" {
|
||||||
|
|
@ -95,7 +113,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
sharedInboxProp.SetIRI(sharedInboxURI)
|
sharedInboxProp.SetIRI(sharedInboxURI)
|
||||||
endpoints.SetActivityStreamsSharedInbox(sharedInboxProp)
|
endpoints.SetActivityStreamsSharedInbox(sharedInboxProp)
|
||||||
endpointsProp.AppendActivityStreamsEndpoints(endpoints)
|
endpointsProp.AppendActivityStreamsEndpoints(endpoints)
|
||||||
person.SetActivityStreamsEndpoints(endpointsProp)
|
accountable.SetActivityStreamsEndpoints(endpointsProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// outbox
|
// outbox
|
||||||
|
|
@ -106,7 +124,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
outboxProp := streams.NewActivityStreamsOutboxProperty()
|
outboxProp := streams.NewActivityStreamsOutboxProperty()
|
||||||
outboxProp.SetIRI(outboxURI)
|
outboxProp.SetIRI(outboxURI)
|
||||||
person.SetActivityStreamsOutbox(outboxProp)
|
accountable.SetActivityStreamsOutbox(outboxProp)
|
||||||
|
|
||||||
// featured posts
|
// featured posts
|
||||||
// Pinned posts.
|
// Pinned posts.
|
||||||
|
|
@ -116,7 +134,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
featuredProp := streams.NewTootFeaturedProperty()
|
featuredProp := streams.NewTootFeaturedProperty()
|
||||||
featuredProp.SetIRI(featuredURI)
|
featuredProp.SetIRI(featuredURI)
|
||||||
person.SetTootFeatured(featuredProp)
|
accountable.SetTootFeatured(featuredProp)
|
||||||
|
|
||||||
// featuredTags
|
// featuredTags
|
||||||
// NOT IMPLEMENTED
|
// NOT IMPLEMENTED
|
||||||
|
|
@ -125,7 +143,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
|
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
|
||||||
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
|
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||||
preferredUsernameProp.SetXMLSchemaString(a.Username)
|
preferredUsernameProp.SetXMLSchemaString(a.Username)
|
||||||
person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
|
accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
|
||||||
|
|
||||||
// name
|
// name
|
||||||
// Used as profile display name.
|
// Used as profile display name.
|
||||||
|
|
@ -135,14 +153,14 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
} else {
|
} else {
|
||||||
nameProp.AppendXMLSchemaString(a.Username)
|
nameProp.AppendXMLSchemaString(a.Username)
|
||||||
}
|
}
|
||||||
person.SetActivityStreamsName(nameProp)
|
accountable.SetActivityStreamsName(nameProp)
|
||||||
|
|
||||||
// summary
|
// summary
|
||||||
// Used as profile bio.
|
// Used as profile bio.
|
||||||
if a.Note != "" {
|
if a.Note != "" {
|
||||||
summaryProp := streams.NewActivityStreamsSummaryProperty()
|
summaryProp := streams.NewActivityStreamsSummaryProperty()
|
||||||
summaryProp.AppendXMLSchemaString(a.Note)
|
summaryProp.AppendXMLSchemaString(a.Note)
|
||||||
person.SetActivityStreamsSummary(summaryProp)
|
accountable.SetActivityStreamsSummary(summaryProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// url
|
// url
|
||||||
|
|
@ -153,19 +171,19 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
}
|
}
|
||||||
urlProp := streams.NewActivityStreamsUrlProperty()
|
urlProp := streams.NewActivityStreamsUrlProperty()
|
||||||
urlProp.AppendIRI(profileURL)
|
urlProp.AppendIRI(profileURL)
|
||||||
person.SetActivityStreamsUrl(urlProp)
|
accountable.SetActivityStreamsUrl(urlProp)
|
||||||
|
|
||||||
// manuallyApprovesFollowers
|
// manuallyApprovesFollowers
|
||||||
// Will be shown as a locked account.
|
// Will be shown as a locked account.
|
||||||
manuallyApprovesFollowersProp := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
|
manuallyApprovesFollowersProp := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
|
||||||
manuallyApprovesFollowersProp.Set(*a.Locked)
|
manuallyApprovesFollowersProp.Set(*a.Locked)
|
||||||
person.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
|
accountable.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
|
||||||
|
|
||||||
// discoverable
|
// discoverable
|
||||||
// Will be shown in the profile directory.
|
// Will be shown in the profile directory.
|
||||||
discoverableProp := streams.NewTootDiscoverableProperty()
|
discoverableProp := streams.NewTootDiscoverableProperty()
|
||||||
discoverableProp.Set(*a.Discoverable)
|
discoverableProp.Set(*a.Discoverable)
|
||||||
person.SetTootDiscoverable(discoverableProp)
|
accountable.SetTootDiscoverable(discoverableProp)
|
||||||
|
|
||||||
// devices
|
// devices
|
||||||
// NOT IMPLEMENTED, probably won't implement
|
// NOT IMPLEMENTED, probably won't implement
|
||||||
|
|
@ -183,7 +201,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
alsoKnownAsURIs[i] = uri
|
alsoKnownAsURIs[i] = uri
|
||||||
}
|
}
|
||||||
|
|
||||||
ap.SetAlsoKnownAs(person, alsoKnownAsURIs)
|
ap.SetAlsoKnownAs(accountable, alsoKnownAsURIs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// movedTo
|
// movedTo
|
||||||
|
|
@ -194,7 +212,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ap.SetMovedTo(person, movedTo)
|
ap.SetMovedTo(accountable, movedTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicKey
|
// publicKey
|
||||||
|
|
@ -235,7 +253,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
|
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
|
||||||
|
|
||||||
// set the public key property on the Person
|
// set the public key property on the Person
|
||||||
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
|
accountable.SetW3IDSecurityV1PublicKey(publicKeyProp)
|
||||||
|
|
||||||
// tags
|
// tags
|
||||||
tagProp := streams.NewActivityStreamsTagProperty()
|
tagProp := streams.NewActivityStreamsTagProperty()
|
||||||
|
|
@ -263,7 +281,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
// tag -- hashtags
|
// tag -- hashtags
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
person.SetActivityStreamsTag(tagProp)
|
accountable.SetActivityStreamsTag(tagProp)
|
||||||
|
|
||||||
// attachment
|
// attachment
|
||||||
// Used for profile fields.
|
// Used for profile fields.
|
||||||
|
|
@ -284,7 +302,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
attachmentProp.AppendSchemaPropertyValue(propertyValue)
|
attachmentProp.AppendSchemaPropertyValue(propertyValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
person.SetActivityStreamsAttachment(attachmentProp)
|
accountable.SetActivityStreamsAttachment(attachmentProp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// endpoints
|
// endpoints
|
||||||
|
|
@ -320,7 +338,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
iconImage.SetActivityStreamsUrl(avatarURLProperty)
|
iconImage.SetActivityStreamsUrl(avatarURLProperty)
|
||||||
|
|
||||||
iconProperty.AppendActivityStreamsImage(iconImage)
|
iconProperty.AppendActivityStreamsImage(iconImage)
|
||||||
person.SetActivityStreamsIcon(iconProperty)
|
accountable.SetActivityStreamsIcon(iconProperty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,20 +372,32 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
|
||||||
headerImage.SetActivityStreamsUrl(headerURLProperty)
|
headerImage.SetActivityStreamsUrl(headerURLProperty)
|
||||||
|
|
||||||
headerProperty.AppendActivityStreamsImage(headerImage)
|
headerProperty.AppendActivityStreamsImage(headerImage)
|
||||||
person.SetActivityStreamsImage(headerProperty)
|
accountable.SetActivityStreamsImage(headerProperty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return person, nil
|
return accountable, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountToASMinimal converts a gts model account into an activity streams person, suitable for federation.
|
// AccountToASMinimal converts a gts model account
|
||||||
|
// into an activity streams person or service.
|
||||||
//
|
//
|
||||||
// The returned account will just have the Type, Username, PublicKey, and ID properties set. This is
|
// The returned account will just have the Type, Username,
|
||||||
// suitable for serving to requesters to whom we want to give as little information as possible because
|
// PublicKey, and ID properties set. This is suitable for
|
||||||
// we don't trust them (yet).
|
// serving to requesters to whom we want to give as little
|
||||||
func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
|
// information as possible because we don't trust them (yet).
|
||||||
person := streams.NewActivityStreamsPerson()
|
func (c *Converter) AccountToASMinimal(
|
||||||
|
ctx context.Context,
|
||||||
|
a *gtsmodel.Account,
|
||||||
|
) (ap.Accountable, error) {
|
||||||
|
// accountable is a service if this
|
||||||
|
// is a bot account, otherwise a person.
|
||||||
|
var accountable ap.Accountable
|
||||||
|
if util.PtrOrZero(a.Bot) {
|
||||||
|
accountable = streams.NewActivityStreamsService()
|
||||||
|
} else {
|
||||||
|
accountable = streams.NewActivityStreamsPerson()
|
||||||
|
}
|
||||||
|
|
||||||
// id should be the activitypub URI of this user
|
// id should be the activitypub URI of this user
|
||||||
// something like https://example.org/users/example_user
|
// something like https://example.org/users/example_user
|
||||||
|
|
@ -377,13 +407,13 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account)
|
||||||
}
|
}
|
||||||
idProp := streams.NewJSONLDIdProperty()
|
idProp := streams.NewJSONLDIdProperty()
|
||||||
idProp.SetIRI(profileIDURI)
|
idProp.SetIRI(profileIDURI)
|
||||||
person.SetJSONLDId(idProp)
|
accountable.SetJSONLDId(idProp)
|
||||||
|
|
||||||
// preferredUsername
|
// preferredUsername
|
||||||
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
|
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
|
||||||
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
|
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
|
||||||
preferredUsernameProp.SetXMLSchemaString(a.Username)
|
preferredUsernameProp.SetXMLSchemaString(a.Username)
|
||||||
person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
|
accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
|
||||||
|
|
||||||
// publicKey
|
// publicKey
|
||||||
// Required for signatures.
|
// Required for signatures.
|
||||||
|
|
@ -423,9 +453,9 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account)
|
||||||
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
|
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
|
||||||
|
|
||||||
// set the public key property on the Person
|
// set the public key property on the Person
|
||||||
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
|
accountable.SetW3IDSecurityV1PublicKey(publicKeyProp)
|
||||||
|
|
||||||
return person, nil
|
return accountable, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusToAS converts a gts model status into an ActivityStreams Statusable implementation, suitable for federation
|
// StatusToAS converts a gts model status into an ActivityStreams Statusable implementation, suitable for federation
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,10 +39,10 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
||||||
testAccount := >smodel.Account{}
|
testAccount := >smodel.Account{}
|
||||||
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
|
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -86,6 +87,7 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
||||||
"owner": "http://localhost:8080/users/the_mighty_zork",
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
|
@ -93,14 +95,80 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
|
||||||
}`, string(bytes))
|
}`, string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *InternalToASTestSuite) TestAccountToASBot() {
|
||||||
|
testAccount := >smodel.Account{}
|
||||||
|
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
|
||||||
|
|
||||||
|
// Update zork to be a bot.
|
||||||
|
testAccount.Bot = util.Ptr(true)
|
||||||
|
if err := suite.state.DB.UpdateAccount(context.Background(), testAccount); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
ser, err := ap.Serialize(accountable)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
suite.Equal(`{
|
||||||
|
"@context": [
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"discoverable": "toot:discoverable",
|
||||||
|
"featured": {
|
||||||
|
"@id": "toot:featured",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"toot": "http://joinmastodon.org/ns#"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discoverable": true,
|
||||||
|
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||||
|
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||||
|
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||||
|
"icon": {
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg"
|
||||||
|
},
|
||||||
|
"id": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
"image": {
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg"
|
||||||
|
},
|
||||||
|
"inbox": "http://localhost:8080/users/the_mighty_zork/inbox",
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"name": "original zork (he/they)",
|
||||||
|
"outbox": "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
|
"preferredUsername": "the_mighty_zork",
|
||||||
|
"publicKey": {
|
||||||
|
"id": "http://localhost:8080/users/the_mighty_zork/main-key",
|
||||||
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
|
"tag": [],
|
||||||
|
"type": "Service",
|
||||||
|
"url": "http://localhost:8080/@the_mighty_zork"
|
||||||
|
}`, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
||||||
testAccount := >smodel.Account{}
|
testAccount := >smodel.Account{}
|
||||||
*testAccount = *suite.testAccounts["local_account_2"]
|
*testAccount = *suite.testAccounts["local_account_2"]
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -150,6 +218,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
|
||||||
"owner": "http://localhost:8080/users/1happyturtle",
|
"owner": "http://localhost:8080/users/1happyturtle",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTc6Jpg6LrRPhVQG4KLz\n2+YqEUUtZPd4YR+TKXuCnwEG9ZNGhgP046xa9h3EWzrZXaOhXvkUQgJuRqPrAcfN\nvc8jBHV2xrUeD8pu/MWKEabAsA/tgCv3nUC47HQ3/c12aHfYoPz3ufWsGGnrkhci\nv8PaveJ3LohO5vjCn1yZ00v6osMJMViEZvZQaazyE9A8FwraIexXabDpoy7tkHRg\nA1fvSkg4FeSG1XMcIz2NN7xyUuFACD+XkuOk7UqzRd4cjPUPLxiDwIsTlcgGOd3E\nUFMWVlPxSGjY2hIKa3lEHytaYK9IMYdSuyCsJshd3/yYC9LqxZY2KdlKJ80VOVyh\nyQIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTc6Jpg6LrRPhVQG4KLz\n2+YqEUUtZPd4YR+TKXuCnwEG9ZNGhgP046xa9h3EWzrZXaOhXvkUQgJuRqPrAcfN\nvc8jBHV2xrUeD8pu/MWKEabAsA/tgCv3nUC47HQ3/c12aHfYoPz3ufWsGGnrkhci\nv8PaveJ3LohO5vjCn1yZ00v6osMJMViEZvZQaazyE9A8FwraIexXabDpoy7tkHRg\nA1fvSkg4FeSG1XMcIz2NN7xyUuFACD+XkuOk7UqzRd4cjPUPLxiDwIsTlcgGOd3E\nUFMWVlPxSGjY2hIKa3lEHytaYK9IMYdSuyCsJshd3/yYC9LqxZY2KdlKJ80VOVyh\nyQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-06-04T13:12:00Z",
|
||||||
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
|
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
|
@ -174,10 +243,10 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -231,6 +300,7 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
|
||||||
"owner": "http://localhost:8080/users/the_mighty_zork",
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
|
@ -243,10 +313,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
||||||
*testAccount = *suite.testAccounts["local_account_2"]
|
*testAccount = *suite.testAccounts["local_account_2"]
|
||||||
testAccount.Fields = testAccount.Fields[0:1] // Take only one field.
|
testAccount.Fields = testAccount.Fields[0:1] // Take only one field.
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -292,6 +362,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
|
||||||
"owner": "http://localhost:8080/users/1happyturtle",
|
"owner": "http://localhost:8080/users/1happyturtle",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTc6Jpg6LrRPhVQG4KLz\n2+YqEUUtZPd4YR+TKXuCnwEG9ZNGhgP046xa9h3EWzrZXaOhXvkUQgJuRqPrAcfN\nvc8jBHV2xrUeD8pu/MWKEabAsA/tgCv3nUC47HQ3/c12aHfYoPz3ufWsGGnrkhci\nv8PaveJ3LohO5vjCn1yZ00v6osMJMViEZvZQaazyE9A8FwraIexXabDpoy7tkHRg\nA1fvSkg4FeSG1XMcIz2NN7xyUuFACD+XkuOk7UqzRd4cjPUPLxiDwIsTlcgGOd3E\nUFMWVlPxSGjY2hIKa3lEHytaYK9IMYdSuyCsJshd3/yYC9LqxZY2KdlKJ80VOVyh\nyQIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTc6Jpg6LrRPhVQG4KLz\n2+YqEUUtZPd4YR+TKXuCnwEG9ZNGhgP046xa9h3EWzrZXaOhXvkUQgJuRqPrAcfN\nvc8jBHV2xrUeD8pu/MWKEabAsA/tgCv3nUC47HQ3/c12aHfYoPz3ufWsGGnrkhci\nv8PaveJ3LohO5vjCn1yZ00v6osMJMViEZvZQaazyE9A8FwraIexXabDpoy7tkHRg\nA1fvSkg4FeSG1XMcIz2NN7xyUuFACD+XkuOk7UqzRd4cjPUPLxiDwIsTlcgGOd3E\nUFMWVlPxSGjY2hIKa3lEHytaYK9IMYdSuyCsJshd3/yYC9LqxZY2KdlKJ80VOVyh\nyQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-06-04T13:12:00Z",
|
||||||
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
|
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
|
@ -304,10 +375,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
||||||
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
|
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
|
||||||
testAccount.Emojis = []*gtsmodel.Emoji{suite.testEmojis["rainbow"]}
|
testAccount.Emojis = []*gtsmodel.Emoji{suite.testEmojis["rainbow"]}
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -353,6 +424,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
|
||||||
"owner": "http://localhost:8080/users/the_mighty_zork",
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
"tag": {
|
"tag": {
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
@ -376,10 +448,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
||||||
sharedInbox := "http://localhost:8080/sharedInbox"
|
sharedInbox := "http://localhost:8080/sharedInbox"
|
||||||
testAccount.SharedInboxURI = &sharedInbox
|
testAccount.SharedInboxURI = &sharedInbox
|
||||||
|
|
||||||
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
ser, err := ap.Serialize(asPerson)
|
ser, err := ap.Serialize(accountable)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(ser, "", " ")
|
bytes, err := json.MarshalIndent(ser, "", " ")
|
||||||
|
|
@ -427,6 +499,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
|
||||||
"owner": "http://localhost:8080/users/the_mighty_zork",
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
},
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
"tag": [],
|
"tag": [],
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
|
|
||||||
|
|
@ -18,68 +18,45 @@
|
||||||
package typeutils
|
package typeutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/superseriousbusiness/activity/pub"
|
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WrapPersonInUpdate ...
|
// WrapAccountableInUpdate wraps the given accountable
|
||||||
func (c *Converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, originAccount *gtsmodel.Account) (vocab.ActivityStreamsUpdate, error) {
|
// in an Update activity with the accountable as the object.
|
||||||
|
//
|
||||||
|
// The Update will be addressed to Public and bcc followers.
|
||||||
|
func (c *Converter) WrapAccountableInUpdate(accountable ap.Accountable) (vocab.ActivityStreamsUpdate, error) {
|
||||||
update := streams.NewActivityStreamsUpdate()
|
update := streams.NewActivityStreamsUpdate()
|
||||||
|
|
||||||
// set the actor
|
// Set actor IRI to this accountable's IRI.
|
||||||
actorURI, err := url.Parse(originAccount.URI)
|
ap.AppendActorIRIs(update, ap.GetJSONLDId(accountable))
|
||||||
if err != nil {
|
|
||||||
return nil, gtserror.Newf("error parsing url %s: %w", originAccount.URI, err)
|
|
||||||
}
|
|
||||||
actorProp := streams.NewActivityStreamsActorProperty()
|
|
||||||
actorProp.AppendIRI(actorURI)
|
|
||||||
update.SetActivityStreamsActor(actorProp)
|
|
||||||
|
|
||||||
// set the ID
|
// Set the update ID
|
||||||
newID, err := id.NewRandomULID()
|
updateURI := uris.GenerateURIForUpdate(ap.ExtractPreferredUsername(accountable), id.NewULID())
|
||||||
if err != nil {
|
ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(update), updateURI)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
idString := uris.GenerateURIForUpdate(originAccount.Username, newID)
|
// Set the accountable as the object of the update.
|
||||||
idURI, err := url.Parse(idString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, gtserror.Newf("error parsing url %s: %w", idString, err)
|
|
||||||
}
|
|
||||||
idProp := streams.NewJSONLDIdProperty()
|
|
||||||
idProp.SetIRI(idURI)
|
|
||||||
update.SetJSONLDId(idProp)
|
|
||||||
|
|
||||||
// set the person as the object here
|
|
||||||
objectProp := streams.NewActivityStreamsObjectProperty()
|
objectProp := streams.NewActivityStreamsObjectProperty()
|
||||||
objectProp.AppendActivityStreamsPerson(person)
|
switch t := accountable.(type) {
|
||||||
|
case vocab.ActivityStreamsPerson:
|
||||||
|
objectProp.AppendActivityStreamsPerson(t)
|
||||||
|
case vocab.ActivityStreamsService:
|
||||||
|
objectProp.AppendActivityStreamsService(t)
|
||||||
|
default:
|
||||||
|
log.Panicf(nil, "%T was neither person nor service", t)
|
||||||
|
}
|
||||||
update.SetActivityStreamsObject(objectProp)
|
update.SetActivityStreamsObject(objectProp)
|
||||||
|
|
||||||
// to should be public
|
// to should be public.
|
||||||
toURI, err := url.Parse(pub.PublicActivityPubIRI)
|
ap.AppendTo(update, ap.PublicURI())
|
||||||
if err != nil {
|
|
||||||
return nil, gtserror.Newf("error parsing url %s: %w", pub.PublicActivityPubIRI, err)
|
|
||||||
}
|
|
||||||
toProp := streams.NewActivityStreamsToProperty()
|
|
||||||
toProp.AppendIRI(toURI)
|
|
||||||
update.SetActivityStreamsTo(toProp)
|
|
||||||
|
|
||||||
// bcc followers
|
// bcc should be followers.
|
||||||
followersURI, err := url.Parse(originAccount.FollowersURI)
|
ap.AppendBcc(update, ap.GetFollowers(accountable))
|
||||||
if err != nil {
|
|
||||||
return nil, gtserror.Newf("error parsing url %s: %w", originAccount.FollowersURI, err)
|
|
||||||
}
|
|
||||||
bccProp := streams.NewActivityStreamsBccProperty()
|
|
||||||
bccProp.AppendIRI(followersURI)
|
|
||||||
update.SetActivityStreamsBcc(bccProp)
|
|
||||||
|
|
||||||
return update, nil
|
return update, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,86 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() {
|
||||||
}`, string(bytes))
|
}`, string(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
|
||||||
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
create, err := suite.typeconverter.WrapAccountableInUpdate(accountable)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
createI, err := ap.Serialize(create)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ID as it's not determinate.
|
||||||
|
createID := ap.GetJSONLDId(create)
|
||||||
|
|
||||||
|
bytes, err := json.MarshalIndent(createI, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Equal(`{
|
||||||
|
"@context": [
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"discoverable": "toot:discoverable",
|
||||||
|
"featured": {
|
||||||
|
"@id": "toot:featured",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"toot": "http://joinmastodon.org/ns#"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actor": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
"bcc": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||||
|
"id": "`+createID.String()+`",
|
||||||
|
"object": {
|
||||||
|
"discoverable": true,
|
||||||
|
"featured": "http://localhost:8080/users/the_mighty_zork/collections/featured",
|
||||||
|
"followers": "http://localhost:8080/users/the_mighty_zork/followers",
|
||||||
|
"following": "http://localhost:8080/users/the_mighty_zork/following",
|
||||||
|
"icon": {
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpg"
|
||||||
|
},
|
||||||
|
"id": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
"image": {
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/original/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg"
|
||||||
|
},
|
||||||
|
"inbox": "http://localhost:8080/users/the_mighty_zork/inbox",
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"name": "original zork (he/they)",
|
||||||
|
"outbox": "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
|
"preferredUsername": "the_mighty_zork",
|
||||||
|
"publicKey": {
|
||||||
|
"id": "http://localhost:8080/users/the_mighty_zork/main-key",
|
||||||
|
"owner": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXTcOAvM1Jiw5Ffpk0qn\nr0cwbNvFe/5zQ+Tp7tumK/ZnT37o7X0FUEXrxNi+dkhmeJ0gsaiN+JQGNUewvpSk\nPIAXKvi908aSfCGjs7bGlJCJCuDuL5d6m7hZnP9rt9fJc70GElPpG0jc9fXwlz7T\nlsPb2ecatmG05Y4jPwdC+oN4MNCv9yQzEvCVMzl76EJaM602kIHC1CISn0rDFmYd\n9rSN7XPlNJw1F6PbpJ/BWQ+pXHKw3OEwNTETAUNYiVGnZU+B7a7bZC9f6/aPbJuV\nt8Qmg+UnDvW1Y8gmfHnxaWG2f5TDBvCHmcYtucIZPLQD4trAozC4ryqlmCWQNKbt\n0wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"published": "2022-05-20T11:09:18Z",
|
||||||
|
"summary": "\u003cp\u003ehey yo this is my profile!\u003c/p\u003e",
|
||||||
|
"tag": [],
|
||||||
|
"type": "Person",
|
||||||
|
"url": "http://localhost:8080/@the_mighty_zork"
|
||||||
|
},
|
||||||
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"type": "Update"
|
||||||
|
}`, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
func TestWrapTestSuite(t *testing.T) {
|
func TestWrapTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(WrapTestSuite))
|
suite.Run(t, new(WrapTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ nav:
|
||||||
- "Configuration":
|
- "Configuration":
|
||||||
- "configuration/index.md"
|
- "configuration/index.md"
|
||||||
- "configuration/general.md"
|
- "configuration/general.md"
|
||||||
|
- "configuration/trusted_proxies.md"
|
||||||
- "configuration/database.md"
|
- "configuration/database.md"
|
||||||
- "configuration/web.md"
|
- "configuration/web.md"
|
||||||
- "configuration/instance.md"
|
- "configuration/instance.md"
|
||||||
|
|
|
||||||
|
|
@ -2853,7 +2853,7 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
|
||||||
"this is a public status, please forward it!",
|
"this is a public status, please forward it!",
|
||||||
"",
|
"",
|
||||||
URLMustParse("http://example.org/users/Some_User"),
|
URLMustParse("http://example.org/users/Some_User"),
|
||||||
[]*url.URL{URLMustParse(pub.PublicActivityPubIRI)},
|
[]*url.URL{ap.PublicURI()},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
[]vocab.ActivityStreamsMention{},
|
[]vocab.ActivityStreamsMention{},
|
||||||
|
|
@ -3207,7 +3207,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"this is a public status, please forward it!",
|
"this is a public status, please forward it!",
|
||||||
"",
|
"",
|
||||||
URLMustParse("http://example.org/users/Some_User"),
|
URLMustParse("http://example.org/users/Some_User"),
|
||||||
[]*url.URL{URLMustParse(pub.PublicActivityPubIRI)},
|
[]*url.URL{ap.PublicURI()},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
[]vocab.ActivityStreamsMention{},
|
[]vocab.ActivityStreamsMention{},
|
||||||
|
|
@ -3228,7 +3228,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"",
|
"",
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
||||||
[]*url.URL{
|
[]*url.URL{
|
||||||
URLMustParse(pub.PublicActivityPubIRI),
|
ap.PublicURI(),
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
|
|
@ -3244,7 +3244,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"",
|
"",
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
||||||
[]*url.URL{
|
[]*url.URL{
|
||||||
URLMustParse(pub.PublicActivityPubIRI),
|
ap.PublicURI(),
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
|
|
@ -3265,7 +3265,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"",
|
"",
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
||||||
[]*url.URL{
|
[]*url.URL{
|
||||||
URLMustParse(pub.PublicActivityPubIRI),
|
ap.PublicURI(),
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
|
|
@ -3286,7 +3286,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"",
|
"",
|
||||||
URLMustParse("https://turnip.farm/users/turniplover6969"),
|
URLMustParse("https://turnip.farm/users/turniplover6969"),
|
||||||
[]*url.URL{
|
[]*url.URL{
|
||||||
URLMustParse(pub.PublicActivityPubIRI),
|
ap.PublicURI(),
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
|
|
@ -3309,7 +3309,7 @@ func NewTestFediStatuses() map[string]vocab.ActivityStreamsNote {
|
||||||
"",
|
"",
|
||||||
URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
|
URLMustParse("http://fossbros-anonymous.io/users/foss_satan"),
|
||||||
[]*url.URL{
|
[]*url.URL{
|
||||||
URLMustParse(pub.PublicActivityPubIRI),
|
ap.PublicURI(),
|
||||||
},
|
},
|
||||||
[]*url.URL{},
|
[]*url.URL{},
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/superseriousbusiness/activity/pub"
|
"github.com/superseriousbusiness/activity/pub"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
|
@ -81,7 +82,7 @@ type MockHTTPClient struct {
|
||||||
// to customize how the client is mocked.
|
// to customize how the client is mocked.
|
||||||
//
|
//
|
||||||
// Note that you should never ever make ACTUAL http calls with this thing.
|
// Note that you should never ever make ACTUAL http calls with this thing.
|
||||||
func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relativeMediaPath string, extraPeople ...vocab.ActivityStreamsPerson) *MockHTTPClient {
|
func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relativeMediaPath string, extraPeople ...ap.Accountable) *MockHTTPClient {
|
||||||
mockHTTPClient := &MockHTTPClient{}
|
mockHTTPClient := &MockHTTPClient{}
|
||||||
|
|
||||||
if do != nil {
|
if do != nil {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,26 @@
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
|
.trusted-proxies-rec {
|
||||||
|
color: $info-fg;
|
||||||
|
background: $info-bg;
|
||||||
|
max-width: fit-content;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
border-radius: $br;
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: $info-bg;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
color: $info-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,29 @@
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/ -}}
|
*/ -}}
|
||||||
|
|
||||||
|
{{- define "trustedProxiesRec" -}}
|
||||||
|
{{- .with }}
|
||||||
|
<div class="trusted-proxies-rec">
|
||||||
|
<p>
|
||||||
|
<strong>Warning!</strong> It looks like <code>trusted-proxies</code> is not set correctly in this instance's configuration.
|
||||||
|
This may cause rate-limiting issues and, by extension, federation issues.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you are the instance admin, you should fix this by adding <code>{{- .trustedProxiesRec -}}</code> to your <code>trusted-proxies</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For more information, see
|
||||||
|
<a
|
||||||
|
href="https://docs.gotosocial.org/en/latest/configuration/trusted_proxies/"
|
||||||
|
rel="nofollow noreferrer noopener"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
the documentation
|
||||||
|
</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
{{- define "thumbnailDescription" -}}
|
{{- define "thumbnailDescription" -}}
|
||||||
{{- if .instance.ThumbnailDescription -}}
|
{{- if .instance.ThumbnailDescription -}}
|
||||||
{{- .instance.ThumbnailDescription -}}
|
{{- .instance.ThumbnailDescription -}}
|
||||||
|
|
@ -56,6 +79,9 @@ Instance Logo
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- with . }}
|
{{- with . }}
|
||||||
|
{{- if .trustedProxiesRec }}
|
||||||
|
{{- template "trustedProxiesRec" . }}
|
||||||
|
{{- end }}
|
||||||
<a aria-label="{{- .instance.Title -}}. Go to instance homepage" href="/" class="nounderline">
|
<a aria-label="{{- .instance.Title -}}. Go to instance homepage" href="/" class="nounderline">
|
||||||
<picture>
|
<picture>
|
||||||
{{- if .instance.ThumbnailStatic }}
|
{{- if .instance.ThumbnailStatic }}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue