diff options
Diffstat (limited to 'internal/federation/federatingdb/move.go')
-rw-r--r-- | internal/federation/federatingdb/move.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/internal/federation/federatingdb/move.go b/internal/federation/federatingdb/move.go new file mode 100644 index 000000000..2e8049e08 --- /dev/null +++ b/internal/federation/federatingdb/move.go @@ -0,0 +1,182 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 gtsmodel contains types used *internally* by GoToSocial and added/removed/selected from the database. +// These types should never be serialized and/or sent out via public APIs, as they contain sensitive information. +// The annotation used on these structs is for handling them via the bun-db ORM. +// See here for more info on bun model annotations: https://bun.uptrace.dev/guide/models.html + +package federatingdb + +import ( + "context" + "errors" + "fmt" + + "codeberg.org/gruf/go-logger/v2/level" + "github.com/superseriousbusiness/activity/streams/vocab" + "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/log" + "github.com/superseriousbusiness/gotosocial/internal/messages" +) + +func (f *federatingDB) Move(ctx context.Context, move vocab.ActivityStreamsMove) error { + if log.Level() >= level.DEBUG { + i, err := marshalItem(move) + if err != nil { + return err + } + l := log.WithContext(ctx). + WithField("move", i) + l.Debug("entering Move") + } + + activityContext := getActivityContext(ctx) + if activityContext.internal { + // Already processed. + return nil + } + + requestingAcct := activityContext.requestingAcct + receivingAcct := activityContext.receivingAcct + + if requestingAcct.IsLocal() { + // We should not be processing + // a Move sent from our own + // instance in the federatingDB. + return nil + } + + // Basic Move requirements we can + // check at this point already: + // + // - Move must have ID/URI set. + // - Move `object` and `actor` must + // be set, and must be the same + // as requesting account. + // - Move `target` must be set, and + // must *not* be the same as + // requesting account. + // - Move `target` and `object` must + // not have been involved in a + // successful Move within the + // last 7 days. + // + // If the Move looks OK at this point, + // additional requirements and checks + // will be processed in FromFediAPI. + + // Ensure ID/URI set. + moveURI := ap.GetJSONLDId(move) + if moveURI == nil { + err := errors.New("Move ID/URI was nil") + return gtserror.SetMalformed(err) + } + moveURIStr := moveURI.String() + + // Check `object` property. + objects := ap.GetObjectIRIs(move) + if l := len(objects); l != 1 { + err := fmt.Errorf("Move requires exactly 1 object, had %d", l) + return gtserror.SetMalformed(err) + } + object := objects[0] + objectStr := object.String() + + if objectStr != requestingAcct.URI { + err := fmt.Errorf( + "Move was signed by %s but object was %s", + requestingAcct.URI, objectStr, + ) + return gtserror.SetMalformed(err) + } + + // Check `actor` property. + actors := ap.GetActorIRIs(move) + if l := len(actors); l != 1 { + err := fmt.Errorf("Move requires exactly 1 actor, had %d", l) + return gtserror.SetMalformed(err) + } + actor := actors[0] + actorStr := actor.String() + + if actorStr != requestingAcct.URI { + err := fmt.Errorf( + "Move was signed by %s but actor was %s", + requestingAcct.URI, actorStr, + ) + return gtserror.SetMalformed(err) + } + + // Check `target` property. + targets := ap.GetTargetIRIs(move) + if l := len(targets); l != 1 { + err := fmt.Errorf("Move requires exactly 1 target, had %d", l) + return gtserror.SetMalformed(err) + } + target := targets[0] + targetStr := target.String() + + if targetStr == requestingAcct.URI { + err := fmt.Errorf( + "Move target and origin were the same (%s)", + targetStr, + ) + return gtserror.SetMalformed(err) + } + + // If movedToURI is set on requestingAcct, + // make sure it points to the intended target. + // + // If it's not set, that's fine, we don't + // need it right now. We know by now that the + // Move was really sent to us by requestingAcct. + movedToURI := receivingAcct.MovedToURI + if movedToURI != "" && + movedToURI != targetStr { + err := fmt.Errorf( + "origin account movedTo is set to %s, which differs from Move target; will not process Move", + movedToURI, + ) + return gtserror.SetMalformed(err) + } + + // Create a stub *gtsmodel.Move with relevant + // values. This will be updated / stored by the + // fedi api worker as necessary. + stubMove := >smodel.Move{ + OriginURI: objectStr, + Origin: object, + TargetURI: targetStr, + Target: target, + URI: moveURIStr, + } + + // We had a Move already or stored a new Move. + // Pass back to a worker for async processing. + f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{ + APObjectType: ap.ObjectProfile, + APActivityType: ap.ActivityMove, + GTSModel: stubMove, + RequestingAccount: requestingAcct, + ReceivingAccount: receivingAcct, + }) + + return nil +} |