[feature] Process outgoing Move from clientAPI (#2750)

* prevent moved accounts from taking create-type actions

* update move logic

* federate move out

* indicate on web profile when an account has moved

* [docs] Add migration docs section

* lock while checking + setting move state

* use redirectFollowers func for clientAPI as well

* comment typo

* linter? i barely know 'er!

* Update internal/uris/uri.go

Co-authored-by: Daenney <daenney@users.noreply.github.com>

* add a couple tests for move

* fix little mistake exposed by tests (thanks tests)

* ensure Move marked as successful

* attach shared util funcs to struct

* lock whole account when doing move

* move moving check to after error check

* replace repeated text with error func

* linterrrrrr!!!!

* catch self follow case

---------

Co-authored-by: Daenney <daenney@users.noreply.github.com>
This commit is contained in:
tobi
2024-03-13 13:53:29 +01:00
committed by GitHub
parent 13b9fd5f92
commit ab2d063fcb
60 changed files with 1124 additions and 309 deletions

View File

@@ -97,6 +97,11 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -72,6 +72,13 @@ func (m *Module) AccountLookupGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.NotFoundAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -81,6 +81,11 @@ func (m *Module) AccountNotePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -113,6 +113,13 @@ func (m *Module) AccountSearchGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -152,6 +152,13 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() && targetAcctID != authed.Account.ID {
// For moving/moved accounts, allow the
// account to view its own statuses only.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
limit := 30
limitString := c.Query(LimitKey)
if limitString != "" {

View File

@@ -99,6 +99,11 @@ func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
form := &apimodel.AdminActionRequest{}
if err := c.ShouldBind(form); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)

View File

@@ -107,6 +107,11 @@ func (m *Module) DomainKeysExpirePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -75,6 +75,11 @@ func (m *Module) createDomainPermissions(
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return
@@ -178,6 +183,11 @@ func (m *Module) deleteDomainPermission(
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -93,6 +93,11 @@ func (m *Module) EmailTestPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -110,6 +110,11 @@ func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -87,6 +87,11 @@ func (m *Module) EmojiDELETEHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -137,6 +137,11 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -114,6 +114,11 @@ func (m *Module) createHeaderFilter(c *gin.Context, create func(context.Context,
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
errWithCode := gtserror.NewErrorNotAcceptable(err, err.Error())
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
@@ -157,6 +162,11 @@ func (m *Module) deleteHeaderFilter(c *gin.Context, delete func(context.Context,
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
filterID, errWithCode := apiutil.ParseID(c.Param("ID"))
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)

View File

@@ -81,6 +81,11 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
form := &apimodel.MediaCleanupRequest{}
if err := c.ShouldBind(form); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)

View File

@@ -83,6 +83,11 @@ func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if errWithCode := m.processor.Admin().MediaRefetch(c.Request.Context(), authed.Account, c.Query(DomainQueryKey)); errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return

View File

@@ -97,6 +97,11 @@ func (m *Module) ReportResolvePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -77,6 +77,11 @@ func (m *Module) RulePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -85,6 +85,11 @@ func (m *Module) RuleDELETEHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -77,6 +77,11 @@ func (m *Module) RulePATCHHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -131,6 +131,11 @@ func (m *Module) FilterPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -137,6 +137,11 @@ func (m *Module) FilterPUTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -75,6 +75,11 @@ func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -144,6 +144,11 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
form := &apimodel.InstanceSettingsUpdateRequest{}
if err := c.ShouldBind(&form); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)

View File

@@ -87,6 +87,11 @@ func (m *Module) ListAccountsPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -74,6 +74,11 @@ func (m *Module) ListCreatePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -104,6 +104,11 @@ func (m *Module) ListUpdatePUTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -108,6 +108,11 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -112,6 +112,11 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -87,6 +87,11 @@ func (m *Module) PollVotePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
errWithCode := gtserror.NewErrorNotAcceptable(err, err.Error())
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)

View File

@@ -72,6 +72,11 @@ func (m *Module) ReportPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -175,6 +175,18 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
results := &apimodel.SearchResult{
Accounts: make([]*apimodel.Account, 0),
Statuses: make([]*apimodel.Status, 0),
Hashtags: make([]any, 0),
}
apiutil.JSON(c, http.StatusOK, results)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -75,6 +75,11 @@ func (m *Module) StatusBookmarkPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -78,6 +78,11 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -218,6 +218,11 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -74,6 +74,11 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -78,6 +78,11 @@ func (m *Module) StatusMutePOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -80,6 +80,11 @@ func (m *Module) StatusPinPOSTHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
apiutil.ForbiddenAfterMove(c)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -185,6 +185,13 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
account = authed.Account
}
if account.IsMoving() {
// Moving accounts can't
// use streaming endpoints.
apiutil.NotFoundAfterMove(c)
return
}
// Get the initial requested stream type, if there is one.
streamType := c.Query(StreamQueryKey)

View File

@@ -113,6 +113,13 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -112,6 +112,13 @@ func (m *Module) ListTimelineGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -124,6 +124,13 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
return
}
if authed.Account != nil && authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return

View File

@@ -114,6 +114,13 @@ func (m *Module) TagTimelineGETHandler(c *gin.Context) {
return
}
if authed.Account.IsMoving() {
// For moving/moved accounts, just return
// empty to avoid breaking client apps.
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
return
}
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return