diff options
author | 2023-01-25 18:06:41 +0100 | |
---|---|---|
committer | 2023-01-25 18:06:41 +0100 | |
commit | 17eecfb6d9f50821d7822d60fa4135d59ea5ca57 (patch) | |
tree | 26796a658eb1b05e27c34ec23c2fc8030ffb4458 /internal | |
parent | [feature] Accept incoming federated Flag activity (#1382) (diff) | |
download | gotosocial-17eecfb6d9f50821d7822d60fa4135d59ea5ca57.tar.xz |
[feature] Public list of suspended domains (#1362)
* basic rendered domain blocklist (unauthenticated!)
* style basic domain block list
* better formatting for domain blocklist
* add opt-in config option for showing suspended domains
* format/linter
* re-use InstancePeersGet for web-accessible domain blocklist
* reword explanation, border styling
* always attach blocklist handler, update error message
* domain blocklist error message grammar
Diffstat (limited to 'internal')
-rw-r--r-- | internal/api/client/instance/instancepeersget.go | 17 | ||||
-rw-r--r-- | internal/config/config.go | 1 | ||||
-rw-r--r-- | internal/config/defaults.go | 1 | ||||
-rw-r--r-- | internal/config/flags.go | 1 | ||||
-rw-r--r-- | internal/config/helpers.gen.go | 25 | ||||
-rw-r--r-- | internal/processing/instance.go | 13 | ||||
-rw-r--r-- | internal/processing/processor.go | 2 | ||||
-rw-r--r-- | internal/web/domain-blocklist.go | 71 | ||||
-rw-r--r-- | internal/web/robots.go | 4 | ||||
-rw-r--r-- | internal/web/web.go | 2 |
10 files changed, 122 insertions, 15 deletions
diff --git a/internal/api/client/instance/instancepeersget.go b/internal/api/client/instance/instancepeersget.go index d3880e2cd..9e2ed89e5 100644 --- a/internal/api/client/instance/instancepeersget.go +++ b/internal/api/client/instance/instancepeersget.go @@ -24,6 +24,7 @@ import ( "strings" apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -105,6 +106,8 @@ func (m *Module) InstancePeersGETHandler(c *gin.Context) { return } + var isUnauthenticated = authed.Account == nil || authed.User == nil + if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet) return @@ -136,7 +139,19 @@ func (m *Module) InstancePeersGETHandler(c *gin.Context) { flat = true } - data, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), authed, includeSuspended, includeOpen, flat) + if includeOpen && !config.GetInstanceExposePeers() && isUnauthenticated { + err := fmt.Errorf("peers open query requires an authenticated account/user") + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + return + } + + if includeSuspended && !config.GetInstanceExposeSuspended() && isUnauthenticated { + err := fmt.Errorf("peers suspended query requires an authenticated account/user") + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + return + } + + data, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), includeSuspended, includeOpen, flat) if errWithCode != nil { apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet) return diff --git a/internal/config/config.go b/internal/config/config.go index c28cfe419..472e7b93d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -76,6 +76,7 @@ type Configuration struct { InstanceExposePeers bool `name:"instance-expose-peers" usage:"Allow unauthenticated users to query /api/v1/instance/peers?filter=open"` InstanceExposeSuspended bool `name:"instance-expose-suspended" usage:"Expose suspended instances via web UI, and allow unauthenticated users to query /api/v1/instance/peers?filter=suspended"` + InstanceExposeSuspendedWeb bool `name:"instance-expose-suspended-web" usage:"Expose list of suspended instances as webpage on /about/suspended"` InstanceExposePublicTimeline bool `name:"instance-expose-public-timeline" usage:"Allow unauthenticated users to query /api/v1/timelines/public"` InstanceDeliverToSharedInboxes bool `name:"instance-deliver-to-shared-inboxes" usage:"Deliver federated messages to shared inboxes, if they're available."` diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 3cd1a3ed3..ecd32be2f 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -58,6 +58,7 @@ var Defaults = Configuration{ InstanceExposePeers: false, InstanceExposeSuspended: false, + InstanceExposeSuspendedWeb: false, InstanceDeliverToSharedInboxes: true, AccountsRegistrationOpen: true, diff --git a/internal/config/flags.go b/internal/config/flags.go index a0fde3eed..a21f17f53 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -78,6 +78,7 @@ func (s *ConfigState) AddServerFlags(cmd *cobra.Command) { // Instance cmd.Flags().Bool(InstanceExposePeersFlag(), cfg.InstanceExposePeers, fieldtag("InstanceExposePeers", "usage")) cmd.Flags().Bool(InstanceExposeSuspendedFlag(), cfg.InstanceExposeSuspended, fieldtag("InstanceExposeSuspended", "usage")) + cmd.Flags().Bool(InstanceExposeSuspendedWebFlag(), cfg.InstanceExposeSuspendedWeb, fieldtag("InstanceExposeSuspendedWeb", "usage")) cmd.Flags().Bool(InstanceDeliverToSharedInboxesFlag(), cfg.InstanceDeliverToSharedInboxes, fieldtag("InstanceDeliverToSharedInboxes", "usage")) // Accounts diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go index 1da2ff42c..3560c566a 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go @@ -724,6 +724,31 @@ func GetInstanceExposeSuspended() bool { return global.GetInstanceExposeSuspende // SetInstanceExposeSuspended safely sets the value for global configuration 'InstanceExposeSuspended' field func SetInstanceExposeSuspended(v bool) { global.SetInstanceExposeSuspended(v) } +// GetInstanceExposeSuspendedWeb safely fetches the Configuration value for state's 'InstanceExposeSuspendedWeb' field +func (st *ConfigState) GetInstanceExposeSuspendedWeb() (v bool) { + st.mutex.Lock() + v = st.config.InstanceExposeSuspendedWeb + st.mutex.Unlock() + return +} + +// SetInstanceExposeSuspendedWeb safely sets the Configuration value for state's 'InstanceExposeSuspendedWeb' field +func (st *ConfigState) SetInstanceExposeSuspendedWeb(v bool) { + st.mutex.Lock() + defer st.mutex.Unlock() + st.config.InstanceExposeSuspendedWeb = v + st.reloadToViper() +} + +// InstanceExposeSuspendedWebFlag returns the flag name for the 'InstanceExposeSuspendedWeb' field +func InstanceExposeSuspendedWebFlag() string { return "instance-expose-suspended-web" } + +// GetInstanceExposeSuspendedWeb safely fetches the value for global configuration 'InstanceExposeSuspendedWeb' field +func GetInstanceExposeSuspendedWeb() bool { return global.GetInstanceExposeSuspendedWeb() } + +// SetInstanceExposeSuspendedWeb safely sets the value for global configuration 'InstanceExposeSuspendedWeb' field +func SetInstanceExposeSuspendedWeb(v bool) { global.SetInstanceExposeSuspendedWeb(v) } + // GetInstanceExposePublicTimeline safely fetches the Configuration value for state's 'InstanceExposePublicTimeline' field func (st *ConfigState) GetInstanceExposePublicTimeline() (v bool) { st.mutex.Lock() diff --git a/internal/processing/instance.go b/internal/processing/instance.go index fec636d2a..afa60ad1e 100644 --- a/internal/processing/instance.go +++ b/internal/processing/instance.go @@ -28,7 +28,6 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/text" "github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/validate" @@ -48,15 +47,10 @@ func (p *processor) InstanceGet(ctx context.Context, domain string) (*apimodel.I return ai, nil } -func (p *processor) InstancePeersGet(ctx context.Context, authed *oauth.Auth, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) { +func (p *processor) InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) { domains := []*apimodel.Domain{} if includeOpen { - if !config.GetInstanceExposePeers() && (authed.Account == nil || authed.User == nil) { - err := fmt.Errorf("peers open query requires an authenticated account/user") - return nil, gtserror.NewErrorUnauthorized(err, err.Error()) - } - instances, err := p.db.GetInstancePeers(ctx, false) if err != nil && err != db.ErrNoEntries { err = fmt.Errorf("error selecting instance peers: %s", err) @@ -70,11 +64,6 @@ func (p *processor) InstancePeersGet(ctx context.Context, authed *oauth.Auth, in } if includeSuspended { - if !config.GetInstanceExposeSuspended() && (authed.Account == nil || authed.User == nil) { - err := fmt.Errorf("peers suspended query requires an authenticated account/user") - return nil, gtserror.NewErrorUnauthorized(err, err.Error()) - } - domainBlocks := []*gtsmodel.DomainBlock{} if err := p.db.GetAll(ctx, &domainBlocks); err != nil && err != db.ErrNoEntries { return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/processing/processor.go b/internal/processing/processor.go index 46634aaaa..68a4c5fa4 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -172,7 +172,7 @@ type Processor interface { // InstanceGet retrieves instance information for serving at api/v1/instance InstanceGet(ctx context.Context, domain string) (*apimodel.Instance, gtserror.WithCode) - InstancePeersGet(ctx context.Context, authed *oauth.Auth, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) + InstancePeersGet(ctx context.Context, includeSuspended bool, includeOpen bool, flat bool) (interface{}, gtserror.WithCode) // InstancePatch updates this instance according to the given form. // // It should already be ascertained that the requesting account is authenticated and an admin. diff --git a/internal/web/domain-blocklist.go b/internal/web/domain-blocklist.go new file mode 100644 index 000000000..b3cd0efe7 --- /dev/null +++ b/internal/web/domain-blocklist.go @@ -0,0 +1,71 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +package web + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +const ( + domainBlockListPath = "/about/suspended" +) + +func (m *Module) domainBlockListGETHandler(c *gin.Context) { + authed, err := oauth.Authed(c, false, false, false, false) + if err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + return + } + + if !config.GetInstanceExposeSuspendedWeb() && (authed.Account == nil || authed.User == nil) { + err := fmt.Errorf("this instance does not expose the list of suspended domains publicly") + apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet) + return + } + + host := config.GetHost() + instance, err := m.processor.InstanceGet(c.Request.Context(), host) + if err != nil { + apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet) + return + } + + domainBlocks, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), true, false, false) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet) + return + } + + c.HTML(http.StatusOK, "domain-blocklist.tmpl", gin.H{ + "instance": instance, + "ogMeta": ogBase(instance), + "blocklist": domainBlocks, + "stylesheets": []string{ + assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css", + }, + "javascript": []string{distPathPrefix + "/frontend.js"}, + }) +} diff --git a/internal/web/robots.go b/internal/web/robots.go index 4b4d6df28..f66438f5d 100644 --- a/internal/web/robots.go +++ b/internal/web/robots.go @@ -49,7 +49,9 @@ Disallow: /emoji/ # panels Disallow: /admin Disallow: /user -Disallow: /settings/` +Disallow: /settings/ +# domain blocklist +Disallow: /about/suspended` ) // robotsGETHandler returns a decent robots.txt that prevents crawling diff --git a/internal/web/web.go b/internal/web/web.go index 00b6d4102..f4529286a 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -100,6 +100,8 @@ func (m *Module) Route(r router.Router, mi ...gin.HandlerFunc) { r.AttachHandler(http.MethodGet, confirmEmailPath, m.confirmEmailGETHandler) r.AttachHandler(http.MethodGet, robotsPath, m.robotsGETHandler) + r.AttachHandler(http.MethodGet, domainBlockListPath, m.domainBlockListGETHandler) + /* Attach redirects from old endpoints to current ones for backwards compatibility */ |