Merge branch 'main' into xvello/import-mutes

This commit is contained in:
Xavier Vello 2025-01-27 17:29:59 +01:00 committed by GitHub
commit f30ab4bae4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
328 changed files with 31571 additions and 115701 deletions

View file

@ -540,8 +540,9 @@ func (suite *TypeUtilsTestSuite) GetProcessor() *processing.Processor {
mediaManager := testrig.NewTestMediaManager(&suite.state)
federator := testrig.NewTestFederator(&suite.state, transportController, mediaManager)
emailSender := testrig.NewEmailSender("../../web/template/", nil)
webPushSender := testrig.NewNoopWebPushSender()
processor := testrig.NewTestProcessor(&suite.state, federator, emailSender, mediaManager)
processor := testrig.NewTestProcessor(&suite.state, federator, emailSender, webPushSender, mediaManager)
testrig.StartWorkers(&suite.state, processor.Workers())
return processor

View file

@ -36,12 +36,24 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/uris"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
)
// AccountToAS converts a gts model account into an activity streams person, suitable for federation
func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
person := streams.NewActivityStreamsPerson()
// AccountToAS converts a gts model account
// into an activity streams person or service.
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
// 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.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
// 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.SetIRI(followingURI)
person.SetActivityStreamsFollowing(followingProp)
accountable.SetActivityStreamsFollowing(followingProp)
// 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.SetIRI(followersURI)
person.SetActivityStreamsFollowers(followersProp)
accountable.SetActivityStreamsFollowers(followersProp)
// inbox
// 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.SetIRI(inboxURI)
person.SetActivityStreamsInbox(inboxProp)
accountable.SetActivityStreamsInbox(inboxProp)
// shared inbox -- only add this if we know for sure it has one
if a.SharedInboxURI != nil && *a.SharedInboxURI != "" {
@ -95,7 +113,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
sharedInboxProp.SetIRI(sharedInboxURI)
endpoints.SetActivityStreamsSharedInbox(sharedInboxProp)
endpointsProp.AppendActivityStreamsEndpoints(endpoints)
person.SetActivityStreamsEndpoints(endpointsProp)
accountable.SetActivityStreamsEndpoints(endpointsProp)
}
// outbox
@ -106,7 +124,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
outboxProp := streams.NewActivityStreamsOutboxProperty()
outboxProp.SetIRI(outboxURI)
person.SetActivityStreamsOutbox(outboxProp)
accountable.SetActivityStreamsOutbox(outboxProp)
// featured posts
// Pinned posts.
@ -116,7 +134,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
featuredProp := streams.NewTootFeaturedProperty()
featuredProp.SetIRI(featuredURI)
person.SetTootFeatured(featuredProp)
accountable.SetTootFeatured(featuredProp)
// featuredTags
// 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.
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
preferredUsernameProp.SetXMLSchemaString(a.Username)
person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
// name
// Used as profile display name.
@ -135,14 +153,14 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
} else {
nameProp.AppendXMLSchemaString(a.Username)
}
person.SetActivityStreamsName(nameProp)
accountable.SetActivityStreamsName(nameProp)
// summary
// Used as profile bio.
if a.Note != "" {
summaryProp := streams.NewActivityStreamsSummaryProperty()
summaryProp.AppendXMLSchemaString(a.Note)
person.SetActivityStreamsSummary(summaryProp)
accountable.SetActivityStreamsSummary(summaryProp)
}
// url
@ -153,19 +171,19 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
}
urlProp := streams.NewActivityStreamsUrlProperty()
urlProp.AppendIRI(profileURL)
person.SetActivityStreamsUrl(urlProp)
accountable.SetActivityStreamsUrl(urlProp)
// manuallyApprovesFollowers
// Will be shown as a locked account.
manuallyApprovesFollowersProp := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
manuallyApprovesFollowersProp.Set(*a.Locked)
person.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
accountable.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp)
// discoverable
// Will be shown in the profile directory.
discoverableProp := streams.NewTootDiscoverableProperty()
discoverableProp.Set(*a.Discoverable)
person.SetTootDiscoverable(discoverableProp)
accountable.SetTootDiscoverable(discoverableProp)
// devices
// NOT IMPLEMENTED, probably won't implement
@ -183,7 +201,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
alsoKnownAsURIs[i] = uri
}
ap.SetAlsoKnownAs(person, alsoKnownAsURIs)
ap.SetAlsoKnownAs(accountable, alsoKnownAsURIs)
}
// movedTo
@ -194,7 +212,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
return nil, err
}
ap.SetMovedTo(person, movedTo)
ap.SetMovedTo(accountable, movedTo)
}
// publicKey
@ -235,7 +253,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
// set the public key property on the Person
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
accountable.SetW3IDSecurityV1PublicKey(publicKeyProp)
// tags
tagProp := streams.NewActivityStreamsTagProperty()
@ -263,7 +281,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
// tag -- hashtags
// TODO
person.SetActivityStreamsTag(tagProp)
accountable.SetActivityStreamsTag(tagProp)
// attachment
// Used for profile fields.
@ -284,7 +302,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
attachmentProp.AppendSchemaPropertyValue(propertyValue)
}
person.SetActivityStreamsAttachment(attachmentProp)
accountable.SetActivityStreamsAttachment(attachmentProp)
}
// endpoints
@ -320,7 +338,7 @@ func (c *Converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab
iconImage.SetActivityStreamsUrl(avatarURLProperty)
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)
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
// suitable for serving to requesters to whom we want to give as little information as possible because
// we don't trust them (yet).
func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error) {
person := streams.NewActivityStreamsPerson()
// The returned account will just have the Type, Username,
// PublicKey, and ID properties set. This is suitable for
// serving to requesters to whom we want to give as little
// information as possible because we don't trust them (yet).
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
// 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.SetIRI(profileIDURI)
person.SetJSONLDId(idProp)
accountable.SetJSONLDId(idProp)
// preferredUsername
// Used for Webfinger lookup. Must be unique on the domain, and must correspond to a Webfinger acct: URI.
preferredUsernameProp := streams.NewActivityStreamsPreferredUsernameProperty()
preferredUsernameProp.SetXMLSchemaString(a.Username)
person.SetActivityStreamsPreferredUsername(preferredUsernameProp)
accountable.SetActivityStreamsPreferredUsername(preferredUsernameProp)
// publicKey
// Required for signatures.
@ -423,9 +453,9 @@ func (c *Converter) AccountToASMinimal(ctx context.Context, a *gtsmodel.Account)
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKey)
// 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

View file

@ -27,6 +27,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@ -38,10 +39,10 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
testAccount := &gtsmodel.Account{}
*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)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -86,6 +87,7 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
"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",
@ -93,14 +95,80 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
}`, string(bytes))
}
func (suite *InternalToASTestSuite) TestAccountToASBot() {
testAccount := &gtsmodel.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() {
testAccount := &gtsmodel.Account{}
*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)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -150,6 +218,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
"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"
},
"published": "2022-06-04T13:12:00Z",
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
"tag": [],
"type": "Person",
@ -174,10 +243,10 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
suite.FailNow(err.Error())
}
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -231,6 +300,7 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
"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",
@ -243,10 +313,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
*testAccount = *suite.testAccounts["local_account_2"]
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)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -292,6 +362,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
"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"
},
"published": "2022-06-04T13:12:00Z",
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
"tag": [],
"type": "Person",
@ -304,10 +375,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
*testAccount = *suite.testAccounts["local_account_1"] // take zork for this test
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)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -353,6 +424,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
"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": {
"icon": {
@ -376,10 +448,10 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
sharedInbox := "http://localhost:8080/sharedInbox"
testAccount.SharedInboxURI = &sharedInbox
asPerson, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
accountable, err := suite.typeconverter.AccountToAS(context.Background(), testAccount)
suite.NoError(err)
ser, err := ap.Serialize(asPerson)
ser, err := ap.Serialize(accountable)
suite.NoError(err)
bytes, err := json.MarshalIndent(ser, "", " ")
@ -427,6 +499,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
"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",

View file

@ -616,6 +616,11 @@ func (c *Converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac
}
func (c *Converter) AppToAPIAppSensitive(ctx context.Context, a *gtsmodel.Application) (*apimodel.Application, error) {
vapidKeyPair, err := c.state.DB.GetVAPIDKeyPair(ctx)
if err != nil {
return nil, gtserror.Newf("error getting VAPID public key: %w", err)
}
return &apimodel.Application{
ID: a.ID,
Name: a.Name,
@ -623,6 +628,7 @@ func (c *Converter) AppToAPIAppSensitive(ctx context.Context, a *gtsmodel.Applic
RedirectURI: a.RedirectURI,
ClientID: a.ClientID,
ClientSecret: a.ClientSecret,
VapidKey: vapidKeyPair.Public,
}, nil
}
@ -1878,6 +1884,12 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated.
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
vapidKeyPair, err := c.state.DB.GetVAPIDKeyPair(ctx)
if err != nil {
return nil, gtserror.Newf("error getting VAPID public key: %w", err)
}
instance.Configuration.VAPID.PublicKey = vapidKeyPair.Public
// registrations
instance.Registrations.Enabled = config.GetAccountsRegistrationOpen()
instance.Registrations.ApprovalRequired = true // always required
@ -2985,3 +2997,36 @@ func (c *Converter) InteractionReqToAPIInteractionReq(
URI: req.URI,
}, nil
}
func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription(
ctx context.Context,
subscription *gtsmodel.WebPushSubscription,
) (*apimodel.WebPushSubscription, error) {
vapidKeyPair, err := c.state.DB.GetVAPIDKeyPair(ctx)
if err != nil {
return nil, gtserror.Newf("error getting VAPID key pair: %w", err)
}
return &apimodel.WebPushSubscription{
ID: subscription.ID,
Endpoint: subscription.Endpoint,
ServerKey: vapidKeyPair.Public,
Alerts: apimodel.WebPushSubscriptionAlerts{
Follow: subscription.NotificationFlags.Get(gtsmodel.NotificationFollow),
FollowRequest: subscription.NotificationFlags.Get(gtsmodel.NotificationFollowRequest),
Favourite: subscription.NotificationFlags.Get(gtsmodel.NotificationFavourite),
Mention: subscription.NotificationFlags.Get(gtsmodel.NotificationMention),
Reblog: subscription.NotificationFlags.Get(gtsmodel.NotificationReblog),
Poll: subscription.NotificationFlags.Get(gtsmodel.NotificationPoll),
Status: subscription.NotificationFlags.Get(gtsmodel.NotificationStatus),
Update: subscription.NotificationFlags.Get(gtsmodel.NotificationUpdate),
AdminSignup: subscription.NotificationFlags.Get(gtsmodel.NotificationAdminSignup),
AdminReport: subscription.NotificationFlags.Get(gtsmodel.NotificationAdminReport),
PendingFavourite: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingFave),
PendingReply: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReply),
PendingReblog: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReblog),
},
Policy: apimodel.WebPushNotificationPolicyAll,
Standard: true,
}, nil
}

View file

@ -21,6 +21,7 @@ import (
"bytes"
"context"
"encoding/json"
"strings"
"testing"
"github.com/stretchr/testify/suite"
@ -2061,6 +2062,13 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
b, err := json.MarshalIndent(instance, "", " ")
suite.NoError(err)
// The VAPID public key changes from run to run.
vapidKeyPair, err := suite.db.GetVAPIDKeyPair(ctx)
if err != nil {
suite.FailNow(err.Error())
}
s := strings.Replace(string(b), vapidKeyPair.Public, "VAPID_PUBLIC_KEY_PLACEHOLDER", 1)
suite.Equal(`{
"domain": "localhost:8080",
"account_domain": "localhost:8080",
@ -2140,6 +2148,9 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
},
"emojis": {
"emoji_size_limit": 51200
},
"vapid": {
"public_key": "VAPID_PUBLIC_KEY_PLACEHOLDER"
}
},
"registrations": {
@ -2184,7 +2195,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
"rules": [],
"terms": "\u003cp\u003eThis is where a list of terms and conditions might go.\u003c/p\u003e\u003cp\u003eFor example:\u003c/p\u003e\u003cp\u003eIf you want to sign up on this instance, you oughta know that we:\u003c/p\u003e\u003col\u003e\u003cli\u003eWill sell your data to whoever offers.\u003c/li\u003e\u003cli\u003eSecure the server with password \u003ccode\u003epassword\u003c/code\u003e wherever possible.\u003c/li\u003e\u003c/ol\u003e",
"terms_text": "This is where a list of terms and conditions might go.\n\nFor example:\n\nIf you want to sign up on this instance, you oughta know that we:\n\n1. Will sell your data to whoever offers.\n2. Secure the server with password `+"`"+`password`+"`"+` wherever possible."
}`, string(b))
}`, s)
}
func (suite *InternalToFrontendTestSuite) TestEmojiToFrontend() {

View file

@ -18,68 +18,45 @@
package typeutils
import (
"net/url"
"github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
"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/log"
"github.com/superseriousbusiness/gotosocial/internal/uris"
)
// WrapPersonInUpdate ...
func (c *Converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, originAccount *gtsmodel.Account) (vocab.ActivityStreamsUpdate, error) {
// WrapAccountableInUpdate wraps the given accountable
// 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()
// set the actor
actorURI, err := url.Parse(originAccount.URI)
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 actor IRI to this accountable's IRI.
ap.AppendActorIRIs(update, ap.GetJSONLDId(accountable))
// set the ID
newID, err := id.NewRandomULID()
if err != nil {
return nil, err
}
// Set the update ID
updateURI := uris.GenerateURIForUpdate(ap.ExtractPreferredUsername(accountable), id.NewULID())
ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(update), updateURI)
idString := uris.GenerateURIForUpdate(originAccount.Username, newID)
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
// Set the accountable as the object of the update.
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)
// to should be public
toURI, err := url.Parse(pub.PublicActivityPubIRI)
if err != nil {
return nil, gtserror.Newf("error parsing url %s: %w", pub.PublicActivityPubIRI, err)
}
toProp := streams.NewActivityStreamsToProperty()
toProp.AppendIRI(toURI)
update.SetActivityStreamsTo(toProp)
// to should be public.
ap.AppendTo(update, ap.PublicURI())
// bcc followers
followersURI, err := url.Parse(originAccount.FollowersURI)
if err != nil {
return nil, gtserror.Newf("error parsing url %s: %w", originAccount.FollowersURI, err)
}
bccProp := streams.NewActivityStreamsBccProperty()
bccProp.AppendIRI(followersURI)
update.SetActivityStreamsBcc(bccProp)
// bcc should be followers.
ap.AppendBcc(update, ap.GetFollowers(accountable))
return update, nil
}

View file

@ -139,6 +139,86 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() {
}`, 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) {
suite.Run(t, new(WrapTestSuite))
}