[feature] Add config option to expose custom emojis without auth (#4233)

# Description

> If this is a code change, please include a summary of what you've coded, and link to the issue(s) it closes/implements.
>
> If this is a documentation change, please briefly describe what you've changed and why.

Does as it says on the tin! Should make things a bit easier for clients that don't provide an access token to the custom emojis endpoint.

Closes https://codeberg.org/superseriousbusiness/gotosocial/issues/2430

## Checklist

Please put an x inside each checkbox to indicate that you've read and followed it: `[ ]` -> `[x]`

If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want).

- [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md).
- [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat.
- [x] I/we have not leveraged AI to create the proposed changes.
- [x] I/we have performed a self-review of added code.
- [x] I/we have written code that is legible and maintainable by others.
- [x] I/we have commented the added code, particularly in hard-to-understand areas.
- [x] I/we have made any necessary changes to documentation.
- [ ] I/we have added tests that cover new code.
- [x] I/we have run tests and they pass locally with the changes.
- [x] I/we have run `go fmt ./...` and `golangci-lint run`.

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4233
Reviewed-by: Daenney <daenney@noreply.codeberg.org>
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Co-committed-by: tobi <tobi.smethurst@protonmail.com>
This commit is contained in:
tobi
2025-06-03 23:30:42 +02:00
committed by tobi
parent be6d80c020
commit 43f1c6d872
11 changed files with 95 additions and 9 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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,13 +57,17 @@ import (
// '500':
// description: internal server error
func (m *Module) CustomEmojisGETHandler(c *gin.Context) {
_, errWithCode := apiutil.TokenAuth(c,
// 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,
)
if errWithCode != nil {
apiutil.ScopeReadCustomEmojis,
); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)

View File

@ -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

View File

@ -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)."`

View File

@ -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,

View File

@ -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" }

View File

@ -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 \

View File

@ -92,6 +92,7 @@ func testDefaults() config.Configuration {
InstanceExposeBlocklistWeb: true,
InstanceExposeAllowlist: true,
InstanceExposeAllowlistWeb: true,
InstanceExposeCustomEmojis: true,
InstanceDeliverToSharedInboxes: true,
InstanceLanguages: language.Languages{
{