Skip to content

Commit

Permalink
[feature] add instance-expose-public-timeline flag (#1039)
Browse files Browse the repository at this point in the history
* Add instance-expose-public-timeline flag

Adds a config flag that allows unauthenticated access to /api/v1/timelines/public. Defaults to false to replicate existing behaviour.

* Update structure following review

* Add comment

* Fix linting
  • Loading branch information
sargant committed Nov 14, 2022
1 parent 8f2d3ca commit d120743
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 22 deletions.
7 changes: 7 additions & 0 deletions docs/configuration/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ instance-expose-peers: false
# Default: false
instance-expose-suspended: false

# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
# to see a list of public posts on this server. Even if set to 'false', then authenticated
# users (members of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-public-timeline: 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.
Expand Down
7 changes: 7 additions & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ instance-expose-peers: false
# Default: false
instance-expose-suspended: false

# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
# to see a list of public posts on this server. Even if set to 'false', then authenticated
# users (members of the instance) will still be able to query the endpoint.
# Options: [true, false]
# Default: false
instance-expose-public-timeline: 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.
Expand Down
13 changes: 12 additions & 1 deletion internal/api/client/timeline/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
Expand Down Expand Up @@ -110,7 +111,17 @@ import (
// '400':
// description: bad request
func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
var authed *oauth.Auth
var err error

if config.GetInstanceExposePublicTimeline() {
// If the public timeline is allowed to be exposed, still check if we
// can extract various authentication properties, but don't require them.
authed, err = oauth.Authed(c, false, false, false, false)
} else {
authed, err = oauth.Authed(c, true, true, true, true)
}

if err != nil {
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,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"`
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."`

AccountsRegistrationOpen bool `name:"accounts-registration-open" usage:"Allow anyone to submit an account signup request. If false, server will be invite-only."`
Expand Down
25 changes: 25 additions & 0 deletions internal/config/helpers.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,31 @@ func GetInstanceExposeSuspended() bool { return global.GetInstanceExposeSuspende
// SetInstanceExposeSuspended safely sets the value for global configuration 'InstanceExposeSuspended' field
func SetInstanceExposeSuspended(v bool) { global.SetInstanceExposeSuspended(v) }

// GetInstanceExposePublicTimeline safely fetches the Configuration value for state's 'InstanceExposePublicTimeline' field
func (st *ConfigState) GetInstanceExposePublicTimeline() (v bool) {
st.mutex.Lock()
v = st.config.InstanceExposePublicTimeline
st.mutex.Unlock()
return
}

// SetInstanceExposePublicTimeline safely sets the Configuration value for state's 'InstanceExposePublicTimeline' field
func (st *ConfigState) SetInstanceExposePublicTimeline(v bool) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.InstanceExposePublicTimeline = v
st.reloadToViper()
}

// InstanceExposePublicTimelineFlag returns the flag name for the 'InstanceExposePublicTimeline' field
func InstanceExposePublicTimelineFlag() string { return "instance-expose-public-timeline" }

// GetInstanceExposePublicTimeline safely fetches the value for global configuration 'InstanceExposePublicTimeline' field
func GetInstanceExposePublicTimeline() bool { return global.GetInstanceExposePublicTimeline() }

// SetInstanceExposePublicTimeline safely sets the value for global configuration 'InstanceExposePublicTimeline' field
func SetInstanceExposePublicTimeline(v bool) { global.SetInstanceExposePublicTimeline(v) }

// GetInstanceDeliverToSharedInboxes safely fetches the Configuration value for state's 'InstanceDeliverToSharedInboxes' field
func (st *ConfigState) GetInstanceDeliverToSharedInboxes() (v bool) {
st.mutex.Lock()
Expand Down
2 changes: 1 addition & 1 deletion internal/db/bundb/timeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI
return statuses, nil
}

func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
func (t *timelineDB) GetPublicTimeline(ctx context.Context, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
// Ensure reasonable
if limit < 0 {
limit = 0
Expand Down
8 changes: 2 additions & 6 deletions internal/db/bundb/timeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,19 @@ type TimelineTestSuite struct {
}

func (suite *TimelineTestSuite) TestGetPublicTimeline() {
viewingAccount := suite.testAccounts["local_account_1"]

s, err := suite.db.GetPublicTimeline(context.Background(), viewingAccount.ID, "", "", "", 20, false)
s, err := suite.db.GetPublicTimeline(context.Background(), "", "", "", 20, false)
suite.NoError(err)

suite.Len(s, 6)
}

func (suite *TimelineTestSuite) TestGetPublicTimelineWithFutureStatus() {
viewingAccount := suite.testAccounts["local_account_1"]

futureStatus := getFutureStatus()
if err := suite.db.Put(context.Background(), futureStatus); err != nil {
suite.FailNow(err.Error())
}

s, err := suite.db.GetPublicTimeline(context.Background(), viewingAccount.ID, "", "", "", 20, false)
s, err := suite.db.GetPublicTimeline(context.Background(), "", "", "", 20, false)
suite.NoError(err)

suite.Len(s, 6)
Expand Down
2 changes: 1 addition & 1 deletion internal/db/timeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type Timeline interface {
// It will use the given filters and try to return as many statuses as possible up to the limit.
//
// Statuses should be returned in descending order of when they were created (newest first).
GetPublicTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, Error)
GetPublicTimeline(ctx context.Context, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, Error)

// GetFavedTimeline fetches the account's FAVED timeline -- ie., posts and replies that the requesting account has faved.
// It will use the given filters and try to return as many statuses as possible up to the limit.
Expand Down
2 changes: 1 addition & 1 deletion internal/processing/statustimeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func (p *processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, max
}

func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
statuses, err := p.db.GetPublicTimeline(ctx, authed.Account.ID, maxID, sinceID, minID, limit, local)
statuses, err := p.db.GetPublicTimeline(ctx, maxID, sinceID, minID, limit, local)
if err != nil {
if err == db.ErrNoEntries {
// there are just no entries left
Expand Down

0 comments on commit d120743

Please sign in to comment.