diff options
Diffstat (limited to 'internal/api/client')
59 files changed, 358 insertions, 29 deletions
diff --git a/internal/api/client/account/account_test.go b/internal/api/client/account/account_test.go index f7dfa4520..b642dbcb4 100644 --- a/internal/api/client/account/account_test.go +++ b/internal/api/client/account/account_test.go @@ -95,5 +95,7 @@ func (suite *AccountStandardTestSuite) newContext(recorder *httptest.ResponseRec ctx.Request.Header.Set("Content-Type", bodyContentType) } + ctx.Request.Header.Set("accept", "application/json") + return ctx } diff --git a/internal/api/client/account/accountcreate.go b/internal/api/client/account/accountcreate.go index ae9d7b0d7..7219fae94 100644 --- a/internal/api/client/account/accountcreate.go +++ b/internal/api/client/account/accountcreate.go @@ -27,6 +27,7 @@ import ( "github.com/spf13/viper" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -78,6 +79,11 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + l.Trace("parsing request form") form := &model.AccountCreateRequest{} if err := c.ShouldBind(form); err != nil || form == nil { diff --git a/internal/api/client/account/accountget.go b/internal/api/client/account/accountget.go index 8bac1360b..2bda3c0bd 100644 --- a/internal/api/client/account/accountget.go +++ b/internal/api/client/account/accountget.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -64,6 +65,11 @@ func (m *Module) AccountGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/accountupdate.go b/internal/api/client/account/accountupdate.go index 8534f5805..2a3882206 100644 --- a/internal/api/client/account/accountupdate.go +++ b/internal/api/client/account/accountupdate.go @@ -20,11 +20,13 @@ package account import ( "fmt" - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -110,6 +112,11 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) { } l.Tracef("retrieved account %+v", authed.Account.ID) + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + form, err := parseUpdateAccountForm(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) diff --git a/internal/api/client/account/accountverify.go b/internal/api/client/account/accountverify.go index 6b8d1f92c..1da0251a3 100644 --- a/internal/api/client/account/accountverify.go +++ b/internal/api/client/account/accountverify.go @@ -19,10 +19,12 @@ package account import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -60,6 +62,11 @@ func (m *Module) AccountVerifyGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + acctSensitive, err := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID) if err != nil { l.Debugf("error getting account from processor: %s", err) diff --git a/internal/api/client/account/block.go b/internal/api/client/account/block.go index 243f90c5e..2de754a33 100644 --- a/internal/api/client/account/block.go +++ b/internal/api/client/account/block.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -66,6 +67,11 @@ func (m *Module) AccountBlockPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/follow.go b/internal/api/client/account/follow.go index 8f3f7ad0a..afdb6d23d 100644 --- a/internal/api/client/account/follow.go +++ b/internal/api/client/account/follow.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -87,6 +88,11 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/followers.go b/internal/api/client/account/followers.go index 4f30e9939..3ccb3d393 100644 --- a/internal/api/client/account/followers.go +++ b/internal/api/client/account/followers.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -68,6 +69,11 @@ func (m *Module) AccountFollowersGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/following.go b/internal/api/client/account/following.go index baac2c9d3..78ab610c8 100644 --- a/internal/api/client/account/following.go +++ b/internal/api/client/account/following.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -68,6 +69,11 @@ func (m *Module) AccountFollowingGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/relationships.go b/internal/api/client/account/relationships.go index bb942d18a..22ae835f0 100644 --- a/internal/api/client/account/relationships.go +++ b/internal/api/client/account/relationships.go @@ -1,10 +1,12 @@ package account import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -57,6 +59,11 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAccountIDs := c.QueryArray("id[]") if len(targetAccountIDs) == 0 { // check fallback -- let's be generous and see if maybe it's just set as 'id'? diff --git a/internal/api/client/account/statuses.go b/internal/api/client/account/statuses.go index 9a60e80ee..49c15987e 100644 --- a/internal/api/client/account/statuses.go +++ b/internal/api/client/account/statuses.go @@ -19,11 +19,13 @@ package account import ( - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -118,6 +120,11 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { l.Debug("no account id specified in query") diff --git a/internal/api/client/account/unblock.go b/internal/api/client/account/unblock.go index 7b16ac887..f075d14ed 100644 --- a/internal/api/client/account/unblock.go +++ b/internal/api/client/account/unblock.go @@ -22,6 +22,7 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -66,6 +67,11 @@ func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) diff --git a/internal/api/client/account/unfollow.go b/internal/api/client/account/unfollow.go index b18903c8a..2ee812f5a 100644 --- a/internal/api/client/account/unfollow.go +++ b/internal/api/client/account/unfollow.go @@ -19,10 +19,12 @@ package account import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -69,6 +71,11 @@ func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetAcctID := c.Param(IDKey) if targetAcctID == "" { l.Debug(err) diff --git a/internal/api/client/admin/domainblockcreate.go b/internal/api/client/admin/domainblockcreate.go index e1b6bb032..dd5623a1c 100644 --- a/internal/api/client/admin/domainblockcreate.go +++ b/internal/api/client/admin/domainblockcreate.go @@ -8,6 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -110,6 +111,11 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + imp := false importString := c.Query(ImportQueryKey) if importString != "" { diff --git a/internal/api/client/admin/domainblockdelete.go b/internal/api/client/admin/domainblockdelete.go index 9c6f97ca2..8d41ec072 100644 --- a/internal/api/client/admin/domainblockdelete.go +++ b/internal/api/client/admin/domainblockdelete.go @@ -5,6 +5,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -62,6 +63,11 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + domainBlockID := c.Param(IDKey) if domainBlockID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"}) diff --git a/internal/api/client/admin/domainblockget.go b/internal/api/client/admin/domainblockget.go index b9dabcd80..49a0795d7 100644 --- a/internal/api/client/admin/domainblockget.go +++ b/internal/api/client/admin/domainblockget.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -63,6 +64,11 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + domainBlockID := c.Param(IDKey) if domainBlockID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"}) diff --git a/internal/api/client/admin/domainblocksget.go b/internal/api/client/admin/domainblocksget.go index 979fc4a69..94b1c89ce 100644 --- a/internal/api/client/admin/domainblocksget.go +++ b/internal/api/client/admin/domainblocksget.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -69,6 +70,11 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + export := false exportString := c.Query(ExportQueryKey) if exportString != "" { diff --git a/internal/api/client/admin/emojicreate.go b/internal/api/client/admin/emojicreate.go index 89325c47a..044e8b643 100644 --- a/internal/api/client/admin/emojicreate.go +++ b/internal/api/client/admin/emojicreate.go @@ -25,6 +25,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -94,6 +95,11 @@ func (m *Module) emojiCreatePOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // extract the media create form from the request context l.Tracef("parsing request form: %+v", c.Request.Form) form := &model.EmojiCreateRequest{} diff --git a/internal/api/client/app/appcreate.go b/internal/api/client/app/appcreate.go index c23a838f4..07ff5a889 100644 --- a/internal/api/client/app/appcreate.go +++ b/internal/api/client/app/appcreate.go @@ -25,6 +25,7 @@ import ( "github.com/sirupsen/logrus" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -81,6 +82,11 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + form := &model.ApplicationCreateRequest{} if err := c.ShouldBind(form); err != nil { c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) diff --git a/internal/api/client/auth/authorize.go b/internal/api/client/auth/authorize.go index 6382f473d..35a681114 100644 --- a/internal/api/client/auth/authorize.go +++ b/internal/api/client/auth/authorize.go @@ -21,14 +21,16 @@ package auth import ( "errors" "fmt" - "github.com/sirupsen/logrus" "net/http" "net/url" "strings" + "github.com/sirupsen/logrus" + "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "github.com/google/uuid" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -41,6 +43,11 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { l := logrus.WithField("func", "AuthorizeGETHandler") s := sessions.Default(c) + if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // UserID will be set in the session by AuthorizePOSTHandler if the caller has already gone through the authentication flow // If it's not set, then we don't know yet who the user is, so we need to redirect them to the sign in page. userID, ok := s.Get(sessionUserID).(string) diff --git a/internal/api/client/auth/signin.go b/internal/api/client/auth/signin.go index 68944226f..cc60a3b52 100644 --- a/internal/api/client/auth/signin.go +++ b/internal/api/client/auth/signin.go @@ -21,11 +21,13 @@ package auth import ( "context" "errors" - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "golang.org/x/crypto/bcrypt" @@ -43,6 +45,12 @@ type login struct { func (m *Module) SignInGETHandler(c *gin.Context) { l := logrus.WithField("func", "SignInGETHandler") l.Trace("entering sign in handler") + + if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + if m.idp != nil { s := sessions.Default(c) diff --git a/internal/api/client/auth/token.go b/internal/api/client/auth/token.go index f24840c9f..d3e362f8f 100644 --- a/internal/api/client/auth/token.go +++ b/internal/api/client/auth/token.go @@ -19,10 +19,12 @@ package auth import ( - "github.com/sirupsen/logrus" "net/http" "net/url" + "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" + "github.com/gin-gonic/gin" ) @@ -41,6 +43,11 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) { l := logrus.WithField("func", "TokenPOSTHandler") l.Trace("entered TokenPOSTHandler") + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + form := &tokenBody{} if err := c.ShouldBind(form); err == nil { c.Request.Form = url.Values{} diff --git a/internal/api/client/blocks/blocksget.go b/internal/api/client/blocks/blocksget.go index b00a6e198..d53feabfe 100644 --- a/internal/api/client/blocks/blocksget.go +++ b/internal/api/client/blocks/blocksget.go @@ -19,11 +19,13 @@ package blocks import ( - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -94,6 +96,11 @@ func (m *Module) BlocksGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + maxID := "" maxIDString := c.Query(MaxIDKey) if maxIDString != "" { diff --git a/internal/api/client/emoji/emojisget.go b/internal/api/client/emoji/emojisget.go index 0feb5d9cc..62223e992 100644 --- a/internal/api/client/emoji/emojisget.go +++ b/internal/api/client/emoji/emojisget.go @@ -4,9 +4,15 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" ) // EmojisGETHandler returns a list of custom emojis enabled on the instance func (m *Module) EmojisGETHandler(c *gin.Context) { + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, []string{}) } diff --git a/internal/api/client/favourites/favouritesget.go b/internal/api/client/favourites/favouritesget.go index 22ed03b39..d112e9b95 100644 --- a/internal/api/client/favourites/favouritesget.go +++ b/internal/api/client/favourites/favouritesget.go @@ -1,11 +1,13 @@ package favourites import ( - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -20,6 +22,11 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + maxID := "" maxIDString := c.Query(MaxIDKey) if maxIDString != "" { diff --git a/internal/api/client/fileserver/servefile.go b/internal/api/client/fileserver/servefile.go index 0840f1d96..287d8af80 100644 --- a/internal/api/client/fileserver/servefile.go +++ b/internal/api/client/fileserver/servefile.go @@ -24,6 +24,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -90,14 +91,14 @@ func (m *FileServer) ServeFile(c *gin.Context) { return } - // TODO: do proper content negotiation here -- if the requester only accepts text/html we should try to serve them *something* + // TODO: if the requester only accepts text/html we should try to serve them *something*. // This is mostly needed because when sharing a link to a gts-hosted file on something like mastodon, the masto servers will // attempt to look up the content to provide a preview of the link, and they ask for text/html. - if c.NegotiateFormat(content.ContentType) == "" { - l.Debugf("couldn't negotiate content for Accept headers %+v: we have content type %s", c.Request.Header.Get("Accepted"), content.ContentType) - c.AbortWithStatus(http.StatusNotAcceptable) + format, err := api.NegotiateAccept(c, api.Offer(content.ContentType)) + if err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) return } - c.DataFromReader(http.StatusOK, content.ContentLength, content.ContentType, bytes.NewReader(content.Content), nil) + c.DataFromReader(http.StatusOK, content.ContentLength, format, bytes.NewReader(content.Content), nil) } diff --git a/internal/api/client/fileserver/servefile_test.go b/internal/api/client/fileserver/servefile_test.go index b93f97606..cf05ebbf1 100644 --- a/internal/api/client/fileserver/servefile_test.go +++ b/internal/api/client/fileserver/servefile_test.go @@ -123,6 +123,7 @@ func (suite *ServeFileTestSuite) TestServeOriginalFileSuccessful() { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.Request = httptest.NewRequest(http.MethodGet, targetAttachment.URL, nil) + ctx.Request.Header.Set("accept", "*/*") // normally the router would populate these params from the path values, // but because we're calling the ServeFile function directly, we need to set them manually. diff --git a/internal/api/client/filter/filtersget.go b/internal/api/client/filter/filtersget.go index 079d39f35..f0367d40c 100644 --- a/internal/api/client/filter/filtersget.go +++ b/internal/api/client/filter/filtersget.go @@ -4,9 +4,15 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" ) // FiltersGETHandler returns a list of filters set by/for the authed account func (m *Module) FiltersGETHandler(c *gin.Context) { + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, []string{}) } diff --git a/internal/api/client/followrequest/authorize.go b/internal/api/client/followrequest/authorize.go index 1ab7891a6..1e1a46711 100644 --- a/internal/api/client/followrequest/authorize.go +++ b/internal/api/client/followrequest/authorize.go @@ -19,10 +19,12 @@ package followrequest import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -76,6 +78,11 @@ func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { l.Debugf("couldn't auth: %s", err) c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"}) diff --git a/internal/api/client/followrequest/followrequest_test.go b/internal/api/client/followrequest/followrequest_test.go index e7dccc210..27b8d3db2 100644 --- a/internal/api/client/followrequest/followrequest_test.go +++ b/internal/api/client/followrequest/followrequest_test.go @@ -106,6 +106,7 @@ func (suite *FollowRequestStandardTestSuite) newContext(recorder *httptest.Respo if bodyContentType != "" { ctx.Request.Header.Set("Content-Type", bodyContentType) } + ctx.Request.Header.Set("accept", "application/json") return ctx } diff --git a/internal/api/client/followrequest/get.go b/internal/api/client/followrequest/get.go index de8c83d9a..036e51c8d 100644 --- a/internal/api/client/followrequest/get.go +++ b/internal/api/client/followrequest/get.go @@ -19,10 +19,12 @@ package followrequest import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -83,6 +85,11 @@ func (m *Module) FollowRequestGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { l.Debugf("couldn't auth: %s", err) c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"}) diff --git a/internal/api/client/followrequest/reject.go b/internal/api/client/followrequest/reject.go index a7fa12e7f..28459b9a6 100644 --- a/internal/api/client/followrequest/reject.go +++ b/internal/api/client/followrequest/reject.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -74,6 +75,11 @@ func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { l.Debugf("couldn't auth: %s", err) c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"}) diff --git a/internal/api/client/instance/instanceget.go b/internal/api/client/instance/instanceget.go index f2f496cb5..4f9afdb38 100644 --- a/internal/api/client/instance/instanceget.go +++ b/internal/api/client/instance/instanceget.go @@ -5,6 +5,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/viper" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/gin-gonic/gin" @@ -35,6 +36,11 @@ import ( func (m *Module) InstanceInformationGETHandler(c *gin.Context) { l := logrus.WithField("func", "InstanceInformationGETHandler") + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + host := viper.GetString(config.Keys.Host) instance, err := m.processor.InstanceGet(c.Request.Context(), host) diff --git a/internal/api/client/instance/instancepatch.go b/internal/api/client/instance/instancepatch.go index 50923dd90..e0200cb0f 100644 --- a/internal/api/client/instance/instancepatch.go +++ b/internal/api/client/instance/instancepatch.go @@ -1,10 +1,12 @@ package instance import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -93,6 +95,11 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // only admins can update instance settings if !authed.User.Admin { l.Debug("user is not an admin so cannot update instance settings") diff --git a/internal/api/client/list/listsgets.go b/internal/api/client/list/listsgets.go index 5d8d7d194..184830290 100644 --- a/internal/api/client/list/listsgets.go +++ b/internal/api/client/list/listsgets.go @@ -4,9 +4,15 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" ) // ListsGETHandler returns a list of lists created by/for the authed account func (m *Module) ListsGETHandler(c *gin.Context) { + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, []string{}) } diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go index f808b554c..47f63561b 100644 --- a/internal/api/client/media/mediacreate.go +++ b/internal/api/client/media/mediacreate.go @@ -27,6 +27,7 @@ import ( "github.com/spf13/viper" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -93,6 +94,11 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // extract the media create form from the request context l.Tracef("parsing request form: %s", c.Request.Form) form := &model.AttachmentRequest{} diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go index f8a17c9de..e87306977 100644 --- a/internal/api/client/media/mediacreate_test.go +++ b/internal/api/client/media/mediacreate_test.go @@ -149,6 +149,7 @@ func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful() } ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", mediamodule.BasePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting ctx.Request.Header.Set("Content-Type", w.FormDataContentType()) + ctx.Request.Header.Set("accept", "application/json") // do the actual request suite.mediaModule.MediaCreatePOSTHandler(ctx) diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go index 0ddccc4e4..42af19000 100644 --- a/internal/api/client/media/mediaget.go +++ b/internal/api/client/media/mediaget.go @@ -19,10 +19,12 @@ package media import ( - "github.com/sirupsen/logrus" "net/http" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -70,6 +72,11 @@ func (m *Module) MediaGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + attachmentID := c.Param(IDKey) if attachmentID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"}) diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go index c22e4e919..97d1e6b6e 100644 --- a/internal/api/client/media/mediaupdate.go +++ b/internal/api/client/media/mediaupdate.go @@ -27,6 +27,7 @@ import ( "github.com/spf13/viper" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -102,6 +103,11 @@ func (m *Module) MediaPUTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + attachmentID := c.Param(IDKey) if attachmentID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"}) diff --git a/internal/api/client/notification/notificationsget.go b/internal/api/client/notification/notificationsget.go index 98d5c2471..5d5aed7a5 100644 --- a/internal/api/client/notification/notificationsget.go +++ b/internal/api/client/notification/notificationsget.go @@ -24,6 +24,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -44,6 +45,11 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + limit := 20 limitString := c.Query(LimitKey) if limitString != "" { diff --git a/internal/api/client/search/searchget.go b/internal/api/client/search/searchget.go index f842e5044..0a33e86ce 100644 --- a/internal/api/client/search/searchget.go +++ b/internal/api/client/search/searchget.go @@ -25,6 +25,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -71,6 +72,11 @@ func (m *Module) SearchGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + accountID := c.Query(AccountIDKey) maxID := c.Query(MaxIDKey) minID := c.Query(MinIDKey) diff --git a/internal/api/client/status/statusboost.go b/internal/api/client/status/statusboost.go index bc950d358..ada791788 100644 --- a/internal/api/client/status/statusboost.go +++ b/internal/api/client/status/statusboost.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -74,13 +75,18 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debug("not authed so can't boost status") c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusboost_test.go b/internal/api/client/status/statusboost_test.go index b5a377565..3fc27becc 100644 --- a/internal/api/client/status/statusboost_test.go +++ b/internal/api/client/status/statusboost_test.go @@ -51,6 +51,7 @@ func (suite *StatusBoostTestSuite) TestPostBoost() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. @@ -117,6 +118,7 @@ func (suite *StatusBoostTestSuite) TestPostUnboostable() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. @@ -155,6 +157,7 @@ func (suite *StatusBoostTestSuite) TestPostNotVisible() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. diff --git a/internal/api/client/status/statuscontext.go b/internal/api/client/status/statuscontext.go index f9d7fcc35..7dc23f570 100644 --- a/internal/api/client/status/statuscontext.go +++ b/internal/api/client/status/statuscontext.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -80,6 +81,11 @@ func (m *Module) StatusContextGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statuscreate.go b/internal/api/client/status/statuscreate.go index 629a325c5..40a437564 100644 --- a/internal/api/client/status/statuscreate.go +++ b/internal/api/client/status/statuscreate.go @@ -27,6 +27,7 @@ import ( "github.com/spf13/viper" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -71,13 +72,18 @@ import ( // description: internal error func (m *Module) StatusCreatePOSTHandler(c *gin.Context) { l := logrus.WithField("func", "statusCreatePOSTHandler") - authed, err := oauth.Authed(c, true, true, true, true) // posting a status is serious business so we want *everything* + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debugf("couldn't auth: %s", err) c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // First check this user/account is permitted to post new statuses. // There's no point continuing otherwise. if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { diff --git a/internal/api/client/status/statuscreate_test.go b/internal/api/client/status/statuscreate_test.go index 776b25769..cbbce681f 100644 --- a/internal/api/client/status/statuscreate_test.go +++ b/internal/api/client/status/statuscreate_test.go @@ -65,6 +65,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {"this is a brand new status! #helloworld"}, "spoiler_text": {"hello hello"}, @@ -119,6 +120,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {statusWithLinksAndTags}, } @@ -154,6 +156,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {"here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow: \n here's an emoji that isn't in the db: :test_emoji: "}, } @@ -195,6 +198,7 @@ func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {"this is a reply to a status that doesn't exist"}, "spoiler_text": {"don't open cuz it won't work"}, @@ -226,6 +230,7 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {fmt.Sprintf("hello @%s this reply should work!", testrig.NewTestAccounts()["local_account_2"].Username)}, "in_reply_to_id": {testrig.NewTestStatuses()["local_account_2_status_1"].ID}, @@ -268,6 +273,7 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "status": {"here's an image attachment"}, "media_ids": {attachment.ID}, diff --git a/internal/api/client/status/statusdelete.go b/internal/api/client/status/statusdelete.go index 5a6b2928e..8e2a81ecb 100644 --- a/internal/api/client/status/statusdelete.go +++ b/internal/api/client/status/statusdelete.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -73,13 +74,18 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debug("not authed so can't delete status") c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusfave.go b/internal/api/client/status/statusfave.go index 7d4879832..3ced93f3a 100644 --- a/internal/api/client/status/statusfave.go +++ b/internal/api/client/status/statusfave.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -70,13 +71,18 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debug("not authed so can't fave status") c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusfave_test.go b/internal/api/client/status/statusfave_test.go index 5b877a291..4c924e7a2 100644 --- a/internal/api/client/status/statusfave_test.go +++ b/internal/api/client/status/statusfave_test.go @@ -55,6 +55,7 @@ func (suite *StatusFaveTestSuite) TestPostFave() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. @@ -103,6 +104,7 @@ func (suite *StatusFaveTestSuite) TestPostUnfaveable() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. diff --git a/internal/api/client/status/statusfavedby.go b/internal/api/client/status/statusfavedby.go index 4de66d65d..a20b7bb8c 100644 --- a/internal/api/client/status/statusfavedby.go +++ b/internal/api/client/status/statusfavedby.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -71,13 +72,18 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, false, false, false, false) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) // we don't really need an app here but we want everything else if err != nil { l.Errorf("error authing status faved by request: %s", err) c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusfavedby_test.go b/internal/api/client/status/statusfavedby_test.go index 0f10d8449..ca85c6ccf 100644 --- a/internal/api/client/status/statusfavedby_test.go +++ b/internal/api/client/status/statusfavedby_test.go @@ -53,6 +53,7 @@ func (suite *StatusFavedByTestSuite) TestGetFavedBy() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. diff --git a/internal/api/client/status/statusget.go b/internal/api/client/status/statusget.go index b246002d8..061434457 100644 --- a/internal/api/client/status/statusget.go +++ b/internal/api/client/status/statusget.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -70,13 +71,18 @@ func (m *Module) StatusGETHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, false, false, false, false) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, false, false, false, false) if err != nil { l.Errorf("error authing status faved by request: %s", err) c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusunboost.go b/internal/api/client/status/statusunboost.go index 7ddb274e0..bb50b3879 100644 --- a/internal/api/client/status/statusunboost.go +++ b/internal/api/client/status/statusunboost.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -71,13 +72,18 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debug("not authed so can't unboost status") c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusunfave.go b/internal/api/client/status/statusunfave.go index 29d3daa47..0b0cebc73 100644 --- a/internal/api/client/status/statusunfave.go +++ b/internal/api/client/status/statusunfave.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -70,13 +71,18 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) { }) l.Debugf("entering function") - authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else + authed, err := oauth.Authed(c, true, true, true, true) if err != nil { l.Debug("not authed so can't unfave status") c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"}) return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + targetStatusID := c.Param(IDKey) if targetStatusID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"}) diff --git a/internal/api/client/status/statusunfave_test.go b/internal/api/client/status/statusunfave_test.go index 0809840da..3355c6326 100644 --- a/internal/api/client/status/statusunfave_test.go +++ b/internal/api/client/status/statusunfave_test.go @@ -56,6 +56,7 @@ func (suite *StatusUnfaveTestSuite) TestPostUnfave() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. @@ -105,6 +106,7 @@ func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting + ctx.Request.Header.Set("accept", "application/json") // normally the router would populate these params from the path values, // but because we're calling the function directly, we need to set them manually. diff --git a/internal/api/client/timeline/home.go b/internal/api/client/timeline/home.go index ef5aa48b6..2dedd9c9e 100644 --- a/internal/api/client/timeline/home.go +++ b/internal/api/client/timeline/home.go @@ -19,11 +19,13 @@ package timeline import ( - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -112,6 +114,11 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + maxID := "" maxIDString := c.Query(MaxIDKey) if maxIDString != "" { diff --git a/internal/api/client/timeline/public.go b/internal/api/client/timeline/public.go index 049078e4d..ae2922a25 100644 --- a/internal/api/client/timeline/public.go +++ b/internal/api/client/timeline/public.go @@ -19,11 +19,13 @@ package timeline import ( - "github.com/sirupsen/logrus" "net/http" "strconv" + "github.com/sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -112,6 +114,11 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + maxID := "" maxIDString := c.Query(MaxIDKey) if maxIDString != "" { diff --git a/internal/api/client/user/passwordchange.go b/internal/api/client/user/passwordchange.go index 581abe526..f17ec7e85 100644 --- a/internal/api/client/user/passwordchange.go +++ b/internal/api/client/user/passwordchange.go @@ -23,6 +23,7 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) @@ -71,6 +72,11 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) { return } + if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil { + c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()}) + return + } + // First check this user/account is active. if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() { l.Debugf("couldn't auth: %s", err) diff --git a/internal/api/client/user/passwordchange_test.go b/internal/api/client/user/passwordchange_test.go index bdbeb3e42..ae513babe 100644 --- a/internal/api/client/user/passwordchange_test.go +++ b/internal/api/client/user/passwordchange_test.go @@ -50,6 +50,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordChangePOST() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil) + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "old_password": {"password"}, "new_password": {"peepeepoopoopassword"}, @@ -83,6 +84,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordMissingOldPassword() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil) + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "new_password": {"peepeepoopoopassword"}, } @@ -109,6 +111,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordIncorrectOldPassword() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil) + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "old_password": {"notright"}, "new_password": {"peepeepoopoopassword"}, @@ -136,6 +139,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordWeakNewPassword() { ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"]) ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil) + ctx.Request.Header.Set("accept", "application/json") ctx.Request.Form = url.Values{ "old_password": {"password"}, "new_password": {"peepeepoopoo"}, |