summaryrefslogtreecommitdiff
path: root/internal/typeutils
diff options
context:
space:
mode:
authorLibravatar tobi <tobi.smethurst@protonmail.com>2025-07-09 16:50:25 +0200
committerLibravatar kim <gruf@noreply.codeberg.org>2025-07-09 16:50:25 +0200
commitdcfc9b7885e7ed4f7886a35ccb3e007c293d3521 (patch)
tree3bef2a25b9c8178bdfbece05a5165c94a5a7ae03 /internal/typeutils
parent[performance] use our own typed value context types for Value() key checking ... (diff)
downloadgotosocial-dcfc9b7885e7ed4f7886a35ccb3e007c293d3521.tar.xz
[feature] Use `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` properties for web visibility of statuses (#4315)
This pull request implements two new properties on ActivityPub actors: `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb`. As documented, these properties allow actors to signal their preference for whether or not their posts should be hidden from unauthenticated web views (ie., web pages like the GtS frontend, web apps like the Mastodon frontend, web APIs like the Mastodon public timeline API, etc). This allows remote accounts to *opt in* to having their unlisted visibility posts shown in (for example) the replies section of the web view of a GtS thread. In future, we can also use these properties to determine whether we should show boosts of a remote actor's post on a GtS profile, and that sort of thing. In keeping with our stance around privacy by default, GtS assumes `true` for `hidesCcPublicFromUnauthedWeb` if the property is not set on a remote actor, ie., hide unlisted/unlocked posts by default. `hidesToPublicFromUnauthedWeb` is assumed to be `false` if the property is not set on a remote actor, ie., show public posts by default. ~~WIP as I still want to work on the documentation for this a bit.~~ New props are already in the namespace document: https://gotosocial.org/ns Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4315 Reviewed-by: kim <gruf@noreply.codeberg.org> Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
Diffstat (limited to 'internal/typeutils')
-rw-r--r--internal/typeutils/astointernal.go4
-rw-r--r--internal/typeutils/internaltoas.go4
-rw-r--r--internal/typeutils/internaltoas_test.go21
-rw-r--r--internal/typeutils/internaltofrontend.go19
-rw-r--r--internal/typeutils/internaltofrontend_test.go4
-rw-r--r--internal/typeutils/wrap_test.go3
6 files changed, 52 insertions, 3 deletions
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go
index 35f1e7cb7..ede2b3253 100644
--- a/internal/typeutils/astointernal.go
+++ b/internal/typeutils/astointernal.go
@@ -244,6 +244,10 @@ func (c *Converter) ASRepresentationToAccount(
acct.PublicKey = pkey
acct.PublicKeyURI = pkeyURL.String()
+ // Web visibility for statuses.
+ acct.HidesToPublicFromUnauthedWeb = util.Ptr(ap.GetHidesToPublicFromUnauthedWeb(accountable))
+ acct.HidesCcPublicFromUnauthedWeb = util.Ptr(ap.GetHidesCcPublicFromUnauthedWeb(accountable))
+
return &acct, nil
}
diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go
index 3feecaa9b..ce1501e1a 100644
--- a/internal/typeutils/internaltoas.go
+++ b/internal/typeutils/internaltoas.go
@@ -399,6 +399,10 @@ func (c *Converter) AccountToAS(
}
}
+ // Web visibility for statuses.
+ ap.SetHidesToPublicFromUnauthedWeb(accountable, *a.HidesToPublicFromUnauthedWeb)
+ ap.SetHidesCcPublicFromUnauthedWeb(accountable, *a.HidesCcPublicFromUnauthedWeb)
+
return accountable, nil
}
diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go
index 5da103582..f3e19bb81 100644
--- a/internal/typeutils/internaltoas_test.go
+++ b/internal/typeutils/internaltoas_test.go
@@ -48,6 +48,7 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -64,6 +65,8 @@ func (suite *InternalToASTestSuite) TestAccountToAS() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",
@@ -116,6 +119,7 @@ func (suite *InternalToASTestSuite) TestAccountToASBot() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -132,6 +136,8 @@ func (suite *InternalToASTestSuite) TestAccountToASBot() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",
@@ -178,6 +184,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -209,6 +216,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithFields() {
"featured": "http://localhost:8080/users/1happyturtle/collections/featured",
"followers": "http://localhost:8080/users/1happyturtle/followers",
"following": "http://localhost:8080/users/1happyturtle/following",
+ "hidesCcPublicFromUnauthedWeb": true,
+ "hidesToPublicFromUnauthedWeb": false,
"id": "http://localhost:8080/users/1happyturtle",
"inbox": "http://localhost:8080/users/1happyturtle/inbox",
"manuallyApprovesFollowers": true,
@@ -256,6 +265,7 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -279,6 +289,8 @@ func (suite *InternalToASTestSuite) TestAccountToASAliasedAndMoved() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",
@@ -328,6 +340,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
// Despite only one field being set, attachments should still be a slice/array.
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -354,6 +367,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithOneField() {
"featured": "http://localhost:8080/users/1happyturtle/collections/featured",
"followers": "http://localhost:8080/users/1happyturtle/followers",
"following": "http://localhost:8080/users/1happyturtle/following",
+ "hidesCcPublicFromUnauthedWeb": true,
+ "hidesToPublicFromUnauthedWeb": false,
"id": "http://localhost:8080/users/1happyturtle",
"inbox": "http://localhost:8080/users/1happyturtle/inbox",
"manuallyApprovesFollowers": true,
@@ -389,6 +404,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -406,6 +422,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithEmoji() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",
@@ -464,6 +482,7 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -483,6 +502,8 @@ func (suite *InternalToASTestSuite) TestAccountToASWithSharedInbox() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index 3b5af6579..aef38ad6e 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -134,9 +134,26 @@ func (c *Converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode
statusContentType = a.Settings.StatusContentType
}
+ // Derive web visibility for
+ // this local account's statuses.
+ var webVisibility apimodel.Visibility
+ switch {
+ case *a.HidesToPublicFromUnauthedWeb:
+ // Hides all.
+ webVisibility = apimodel.VisibilityNone
+
+ case !*a.HidesCcPublicFromUnauthedWeb:
+ // Shows unlisted + public (Masto default).
+ webVisibility = apimodel.VisibilityUnlisted
+
+ default:
+ // Shows public only (GtS default).
+ webVisibility = apimodel.VisibilityPublic
+ }
+
apiAccount.Source = &apimodel.Source{
Privacy: VisToAPIVis(a.Settings.Privacy),
- WebVisibility: VisToAPIVis(a.Settings.WebVisibility),
+ WebVisibility: webVisibility,
WebLayout: a.Settings.WebLayout.String(),
Sensitive: *a.Settings.Sensitive,
Language: a.Settings.Language,
diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go
index 8b0d15f10..a26ff114e 100644
--- a/internal/typeutils/internaltofrontend_test.go
+++ b/internal/typeutils/internaltofrontend_test.go
@@ -965,7 +965,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownAttachments
"in_reply_to_account_id": "01F8MH17FWEB39HZJ76B6VXSKF",
"sensitive": true,
"spoiler_text": "some unknown media included",
- "visibility": "public",
+ "visibility": "unlisted",
"language": "en",
"uri": "http://example.org/users/Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
"url": "http://example.org/@Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
@@ -1114,7 +1114,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
"in_reply_to_account_id": "01F8MH17FWEB39HZJ76B6VXSKF",
"sensitive": true,
"spoiler_text": "some unknown media included",
- "visibility": "public",
+ "visibility": "unlisted",
"language": "en",
"uri": "http://example.org/users/Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
"url": "http://example.org/@Some_User/statuses/01HE7XJ1CG84TBKH5V9XKBVGF5",
diff --git a/internal/typeutils/wrap_test.go b/internal/typeutils/wrap_test.go
index 828e6a683..c0c51b37a 100644
--- a/internal/typeutils/wrap_test.go
+++ b/internal/typeutils/wrap_test.go
@@ -178,6 +178,7 @@ func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
suite.Equal(`{
"@context": [
+ "https://gotosocial.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
{
@@ -198,6 +199,8 @@ func (suite *WrapTestSuite) TestWrapAccountableInUpdate() {
"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",
+ "hidesCcPublicFromUnauthedWeb": false,
+ "hidesToPublicFromUnauthedWeb": false,
"icon": {
"mediaType": "image/jpeg",
"name": "a green goblin looking nasty",