diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 52d024304..3ef284ef1 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -3752,6 +3752,7 @@ info: read:applications: grants read access to user-managed applications read:blocks: grants read access to blocks read:bookmarks: grants read access to bookmarks + read:custom_emojis: grants read access to custom emojis read:favourites: grants read access to accounts read:filters: grants read access to filters read:follows: grants read access to follows @@ -8126,6 +8127,7 @@ paths: - conversations /api/v1/custom_emojis: get: + description: If the instance config setting `instance-expose-custom-emojis` is `true` then authentication is not required. operationId: customEmojisGet produces: - application/json @@ -8143,7 +8145,8 @@ paths: "500": description: internal server error security: - - OAuth2 Bearer: [] + - OAuth2 Bearer: + - read:custom_emojis summary: Get an array of custom emojis available on the instance. tags: - custom_emojis @@ -13588,6 +13591,7 @@ securityDefinitions: read:applications: grants read access to user-managed applications read:blocks: grants read access to blocks read:bookmarks: grants read access to bookmarks + read:custom_emojis: grants read access to custom emojis read:favourites: grants read access to accounts read:filters: grants read access to filters read:follows: grants read access to follows diff --git a/docs/configuration/instance.md b/docs/configuration/instance.md index 1b8282a27..3c856634b 100644 --- a/docs/configuration/instance.md +++ b/docs/configuration/instance.md @@ -139,6 +139,21 @@ instance-expose-blocklist-web: false # Default: false instance-expose-public-timeline: false +# Bool. Allow unauthenticated access to /api/v1/custom_emojis, which +# exposes the list of custom emojis available to users on this server. +# +# Setting this to 'true' may alleviate issues with clients that do not +# provide an access token in their requests to the emojis endpoint, but +# it also means that anyone can look at what emojis are installed on your +# instance, which may present privacy / safety issues. +# +# Even if set to 'false', authenticated users (ie., instance members) +# will still be able to query the endpoint using an access token. +# +# Options: [true, false] +# Default: false +instance-expose-custom-emojis: false + # Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages # to the shared inbox of a recipient, if one is available, instead of delivering # each message to each actor who should receive a message individually. diff --git a/docs/swagger.go b/docs/swagger.go index 500abd7a2..c549a3de3 100644 --- a/docs/swagger.go +++ b/docs/swagger.go @@ -41,6 +41,7 @@ // - read:applications: grants read access to user-managed applications // - read:blocks: grants read access to blocks // - read:bookmarks: grants read access to bookmarks +// - read:custom_emojis: grants read access to custom emojis // - read:favourites: grants read access to accounts // - read:filters: grants read access to filters // - read:follows: grants read access to follows @@ -99,6 +100,7 @@ // read:applications: grants read access to user-managed applications // read:blocks: grants read access to blocks // read:bookmarks: grants read access to bookmarks +// read:custom_emojis: grants read access to custom emojis // read:favourites: grants read access to accounts // read:filters: grants read access to filters // read:follows: grants read access to follows diff --git a/example/config.yaml b/example/config.yaml index 258285465..742bf891b 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -425,6 +425,21 @@ instance-expose-blocklist-web: false # Default: false instance-expose-public-timeline: false +# Bool. Allow unauthenticated access to /api/v1/custom_emojis, which +# exposes the list of custom emojis available to users on this server. +# +# Setting this to 'true' may alleviate issues with clients that do not +# provide an access token in their requests to the emojis endpoint, but +# it also means that anyone can look at what emojis are installed on your +# instance, which may present privacy / safety issues. +# +# Even if set to 'false', authenticated users (ie., instance members) +# will still be able to query the endpoint using an access token. +# +# Options: [true, false] +# Default: false +instance-expose-custom-emojis: false + # Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages # to the shared inbox of a recipient, if one is available, instead of delivering # each message to each actor who should receive a message individually. diff --git a/internal/api/client/customemojis/customemojisget.go b/internal/api/client/customemojis/customemojisget.go index c63445aef..9aa878274 100644 --- a/internal/api/client/customemojis/customemojisget.go +++ b/internal/api/client/customemojis/customemojisget.go @@ -21,6 +21,7 @@ import ( "net/http" apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util" + "code.superseriousbusiness.org/gotosocial/internal/config" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "github.com/gin-gonic/gin" ) @@ -29,6 +30,8 @@ import ( // // Get an array of custom emojis available on the instance. // +// If the instance config setting `instance-expose-custom-emojis` is `true` then authentication is not required. +// // --- // tags: // - custom_emojis @@ -37,7 +40,8 @@ import ( // - application/json // // security: -// - OAuth2 Bearer: [] +// - OAuth2 Bearer: +// - read:custom_emojis // // responses: // '200': @@ -53,12 +57,16 @@ import ( // '500': // description: internal server error func (m *Module) CustomEmojisGETHandler(c *gin.Context) { - _, errWithCode := apiutil.TokenAuth(c, - true, true, true, true, - ) - if errWithCode != nil { - apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) - return + // If custom emojis are not exposed to unauthed + // callers, fail if a token was not provided. + if !config.GetInstanceExposeCustomEmojis() { + if _, errWithCode := apiutil.TokenAuth(c, + true, true, true, true, + apiutil.ScopeReadCustomEmojis, + ); errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) + return + } } if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { diff --git a/internal/api/util/scopes.go b/internal/api/util/scopes.go index 594a46ecd..492fa9dad 100644 --- a/internal/api/util/scopes.go +++ b/internal/api/util/scopes.go @@ -31,6 +31,7 @@ const ( scopeBlocks = "blocks" scopeBookmarks = "bookmarks" scopeConversations = "conversations" + scopeCustomEmojis = "custom_emojis" scopeDomainAllows = "domain_allows" scopeDomainBlocks = "domain_blocks" scopeFavourites = "favourites" @@ -65,6 +66,7 @@ const ( ScopeReadBookmarks Scope = ScopeRead + ":" + scopeBookmarks ScopeWriteBookmarks Scope = ScopeWrite + ":" + scopeBookmarks ScopeWriteConversations Scope = ScopeWrite + ":" + scopeConversations + ScopeReadCustomEmojis Scope = ScopeRead + ":" + scopeCustomEmojis ScopeReadFavourites Scope = ScopeRead + ":" + scopeFavourites ScopeWriteFavourites Scope = ScopeWrite + ":" + scopeFavourites ScopeReadFilters Scope = ScopeRead + ":" + scopeFilters diff --git a/internal/config/config.go b/internal/config/config.go index 5360389af..1e907e2cb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -96,6 +96,7 @@ type Configuration struct { InstanceExposeAllowlist bool `name:"instance-expose-allowlist" usage:"Expose list of allowed domains via web UI, and allow unauthenticated users to query /api/v1/instance/peers?filter=allowed and /api/v1/instance/domain_allows"` InstanceExposeAllowlistWeb bool `name:"instance-expose-allowlist-web" usage:"Expose list of explicitly allowed domains as webpage on /about/domain_allows"` InstanceExposePublicTimeline bool `name:"instance-expose-public-timeline" usage:"Allow unauthenticated users to query /api/v1/timelines/public"` + InstanceExposeCustomEmojis bool `name:"instance-expose-custom-emojis" usage:"Allow unauthenticated access to /api/v1/custom_emojis"` InstanceDeliverToSharedInboxes bool `name:"instance-deliver-to-shared-inboxes" usage:"Deliver federated messages to shared inboxes, if they're available."` InstanceInjectMastodonVersion bool `name:"instance-inject-mastodon-version" usage:"This injects a Mastodon compatible version in /api/v1/instance to help Mastodon clients that use that version for feature detection"` InstanceLanguages language.Languages `name:"instance-languages" usage:"BCP47 language tags for the instance. Used to indicate the preferred languages of instance residents (in order from most-preferred to least-preferred)."` diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 43168a471..c960d526c 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -63,6 +63,7 @@ var Defaults = Configuration{ InstanceExposePeers: false, InstanceExposeBlocklist: false, InstanceExposeBlocklistWeb: false, + InstanceExposeCustomEmojis: false, InstanceDeliverToSharedInboxes: true, InstanceLanguages: make(language.Languages, 0), InstanceSubscriptionsProcessFrom: "23:00", // 11pm, diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index dd584e2f0..cee2c3ad2 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -68,6 +68,7 @@ func (cfg *Configuration) RegisterFlags(flags *pflag.FlagSet) { flags.Bool("instance-expose-allowlist", cfg.InstanceExposeAllowlist, "Expose list of allowed domains via web UI, and allow unauthenticated users to query /api/v1/instance/peers?filter=allowed and /api/v1/instance/domain_allows") flags.Bool("instance-expose-allowlist-web", cfg.InstanceExposeAllowlistWeb, "Expose list of explicitly allowed domains as webpage on /about/domain_allows") flags.Bool("instance-expose-public-timeline", cfg.InstanceExposePublicTimeline, "Allow unauthenticated users to query /api/v1/timelines/public") + flags.Bool("instance-expose-custom-emojis", cfg.InstanceExposeCustomEmojis, "Allow unauthenticated access to /api/v1/custom_emojis") flags.Bool("instance-deliver-to-shared-inboxes", cfg.InstanceDeliverToSharedInboxes, "Deliver federated messages to shared inboxes, if they're available.") flags.Bool("instance-inject-mastodon-version", cfg.InstanceInjectMastodonVersion, "This injects a Mastodon compatible version in /api/v1/instance to help Mastodon clients that use that version for feature detection") flags.StringSlice("instance-languages", cfg.InstanceLanguages.Strings(), "BCP47 language tags for the instance. Used to indicate the preferred languages of instance residents (in order from most-preferred to least-preferred).") @@ -212,7 +213,7 @@ func (cfg *Configuration) RegisterFlags(flags *pflag.FlagSet) { } func (cfg *Configuration) MarshalMap() map[string]any { - cfgmap := make(map[string]any, 186) + cfgmap := make(map[string]any, 188) cfgmap["log-level"] = cfg.LogLevel cfgmap["log-timestamp-format"] = cfg.LogTimestampFormat cfgmap["log-db-queries"] = cfg.LogDbQueries @@ -252,6 +253,7 @@ func (cfg *Configuration) MarshalMap() map[string]any { cfgmap["instance-expose-allowlist"] = cfg.InstanceExposeAllowlist cfgmap["instance-expose-allowlist-web"] = cfg.InstanceExposeAllowlistWeb cfgmap["instance-expose-public-timeline"] = cfg.InstanceExposePublicTimeline + cfgmap["instance-expose-custom-emojis"] = cfg.InstanceExposeCustomEmojis cfgmap["instance-deliver-to-shared-inboxes"] = cfg.InstanceDeliverToSharedInboxes cfgmap["instance-inject-mastodon-version"] = cfg.InstanceInjectMastodonVersion cfgmap["instance-languages"] = cfg.InstanceLanguages.Strings() @@ -724,6 +726,14 @@ func (cfg *Configuration) UnmarshalMap(cfgmap map[string]any) error { } } + if ival, ok := cfgmap["instance-expose-custom-emojis"]; ok { + var err error + cfg.InstanceExposeCustomEmojis, err = cast.ToBoolE(ival) + if err != nil { + return fmt.Errorf("error casting %#v -> bool for 'instance-expose-custom-emojis': %w", ival, err) + } + } + if ival, ok := cfgmap["instance-deliver-to-shared-inboxes"]; ok { var err error cfg.InstanceDeliverToSharedInboxes, err = cast.ToBoolE(ival) @@ -2917,6 +2927,31 @@ func GetInstanceExposePublicTimeline() bool { return global.GetInstanceExposePub // SetInstanceExposePublicTimeline safely sets the value for global configuration 'InstanceExposePublicTimeline' field func SetInstanceExposePublicTimeline(v bool) { global.SetInstanceExposePublicTimeline(v) } +// InstanceExposeCustomEmojisFlag returns the flag name for the 'InstanceExposeCustomEmojis' field +func InstanceExposeCustomEmojisFlag() string { return "instance-expose-custom-emojis" } + +// GetInstanceExposeCustomEmojis safely fetches the Configuration value for state's 'InstanceExposeCustomEmojis' field +func (st *ConfigState) GetInstanceExposeCustomEmojis() (v bool) { + st.mutex.RLock() + v = st.config.InstanceExposeCustomEmojis + st.mutex.RUnlock() + return +} + +// SetInstanceExposeCustomEmojis safely sets the Configuration value for state's 'InstanceExposeCustomEmojis' field +func (st *ConfigState) SetInstanceExposeCustomEmojis(v bool) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.InstanceExposeCustomEmojis = v + st.reloadToViper() +} + +// GetInstanceExposeCustomEmojis safely fetches the value for global configuration 'InstanceExposeCustomEmojis' field +func GetInstanceExposeCustomEmojis() bool { return global.GetInstanceExposeCustomEmojis() } + +// SetInstanceExposeCustomEmojis safely sets the value for global configuration 'InstanceExposeCustomEmojis' field +func SetInstanceExposeCustomEmojis(v bool) { global.SetInstanceExposeCustomEmojis(v) } + // InstanceDeliverToSharedInboxesFlag returns the flag name for the 'InstanceDeliverToSharedInboxes' field func InstanceDeliverToSharedInboxesFlag() string { return "instance-deliver-to-shared-inboxes" } diff --git a/test/envparsing.sh b/test/envparsing.sh index d15551f79..d85f0b940 100755 --- a/test/envparsing.sh +++ b/test/envparsing.sh @@ -114,6 +114,7 @@ EXPECT=$(cat << "EOF" "instance-expose-allowlist-web": true, "instance-expose-blocklist": true, "instance-expose-blocklist-web": true, + "instance-expose-custom-emojis": true, "instance-expose-peers": true, "instance-expose-public-timeline": true, "instance-federation-mode": "allowlist", @@ -246,6 +247,7 @@ GTS_INSTANCE_EXPOSE_BLOCKLIST=true \ GTS_INSTANCE_EXPOSE_BLOCKLIST_WEB=true \ GTS_INSTANCE_EXPOSE_ALLOWLIST=true \ GTS_INSTANCE_EXPOSE_ALLOWLIST_WEB=true \ +GTS_INSTANCE_EXPOSE_CUSTOM_EMOJIS=true \ GTS_INSTANCE_EXPOSE_PUBLIC_TIMELINE=true \ GTS_INSTANCE_FEDERATION_MODE='allowlist' \ GTS_INSTANCE_FEDERATION_SPAM_FILTER=true \ diff --git a/testrig/config.go b/testrig/config.go index e7a594996..b8471244e 100644 --- a/testrig/config.go +++ b/testrig/config.go @@ -92,6 +92,7 @@ func testDefaults() config.Configuration { InstanceExposeBlocklistWeb: true, InstanceExposeAllowlist: true, InstanceExposeAllowlistWeb: true, + InstanceExposeCustomEmojis: true, InstanceDeliverToSharedInboxes: true, InstanceLanguages: language.Languages{ {