Browse Source
* remove type switch in Create() and instead move to FederatedCallbacks()
* add missing (my bad!) federating wrapped callbacks behaviour
* add missing license header 😇
* fix create flag test to use correct function
pull/3706/head
14 changed files with 610 additions and 433 deletions
@ -0,0 +1,87 @@
|
||||
// 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 federatingdb |
||||
|
||||
import ( |
||||
"context" |
||||
"net/http" |
||||
|
||||
"github.com/superseriousbusiness/activity/streams/vocab" |
||||
"github.com/superseriousbusiness/gotosocial/internal/ap" |
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror" |
||||
"github.com/superseriousbusiness/gotosocial/internal/id" |
||||
"github.com/superseriousbusiness/gotosocial/internal/log" |
||||
"github.com/superseriousbusiness/gotosocial/internal/messages" |
||||
) |
||||
|
||||
func (f *federatingDB) Block(ctx context.Context, blockable vocab.ActivityStreamsBlock) error { |
||||
log.DebugKV(ctx, "block", serialize{blockable}) |
||||
|
||||
// Extract relevant values from passed ctx.
|
||||
activityContext := getActivityContext(ctx) |
||||
if activityContext.internal { |
||||
return nil // Already processed.
|
||||
} |
||||
|
||||
requesting := activityContext.requestingAcct |
||||
receiving := activityContext.receivingAcct |
||||
|
||||
if receiving.IsMoving() { |
||||
// A Moving account
|
||||
// can't do this.
|
||||
return nil |
||||
} |
||||
|
||||
// Convert received AS block type to internal model.
|
||||
block, err := f.converter.ASBlockToBlock(ctx, blockable) |
||||
if err != nil { |
||||
err := gtserror.Newf("error converting from AS type: %w", err) |
||||
return gtserror.WrapWithCode(http.StatusBadRequest, err) |
||||
} |
||||
|
||||
// Ensure block enacted by correct account.
|
||||
if block.AccountID != requesting.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "requester %s is not expected actor %s", |
||||
requesting.URI, block.Account.URI) |
||||
} |
||||
|
||||
// Ensure block received by correct account.
|
||||
if block.TargetAccountID != receiving.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "receiver %s is not expected object %s", |
||||
receiving.URI, block.TargetAccount.URI) |
||||
} |
||||
|
||||
// Generate new ID for block.
|
||||
block.ID = id.NewULID() |
||||
|
||||
// Insert the new validated block into the database.
|
||||
if err := f.state.DB.PutBlock(ctx, block); err != nil { |
||||
return gtserror.Newf("error inserting %s into db: %w", block.URI, err) |
||||
} |
||||
|
||||
// Push message to worker queue to handle block side-effects.
|
||||
f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ |
||||
APObjectType: ap.ActivityBlock, |
||||
APActivityType: ap.ActivityCreate, |
||||
GTSModel: block, |
||||
Receiving: receiving, |
||||
Requesting: requesting, |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,91 @@
|
||||
// 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 federatingdb |
||||
|
||||
import ( |
||||
"context" |
||||
"net/http" |
||||
|
||||
"github.com/miekg/dns" |
||||
"github.com/superseriousbusiness/activity/streams/vocab" |
||||
"github.com/superseriousbusiness/gotosocial/internal/ap" |
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror" |
||||
"github.com/superseriousbusiness/gotosocial/internal/id" |
||||
"github.com/superseriousbusiness/gotosocial/internal/log" |
||||
"github.com/superseriousbusiness/gotosocial/internal/messages" |
||||
) |
||||
|
||||
func (f *federatingDB) Flag(ctx context.Context, flaggable vocab.ActivityStreamsFlag) error { |
||||
log.DebugKV(ctx, "flag", serialize{flaggable}) |
||||
|
||||
// Mark activity as handled.
|
||||
f.storeActivityID(flaggable) |
||||
|
||||
// Extract relevant values from passed ctx.
|
||||
activityContext := getActivityContext(ctx) |
||||
if activityContext.internal { |
||||
return nil // Already processed.
|
||||
} |
||||
|
||||
requesting := activityContext.requestingAcct |
||||
receiving := activityContext.receivingAcct |
||||
|
||||
// Convert received AS flag type to internal report model.
|
||||
report, err := f.converter.ASFlagToReport(ctx, flaggable) |
||||
if err != nil { |
||||
err := gtserror.Newf("error converting from AS type: %w", err) |
||||
return gtserror.WrapWithCode(http.StatusBadRequest, err) |
||||
} |
||||
|
||||
// Requesting acc's domain must be at
|
||||
// least a subdomain of the reporting
|
||||
// account. i.e. if they're using a
|
||||
// different account domain to host.
|
||||
if dns.CompareDomainName( |
||||
requesting.Domain, |
||||
report.Account.Domain, |
||||
) < 2 { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "requester %s does not share a domain with Flag Actor account %s", |
||||
requesting.URI, report.Account.URI) |
||||
} |
||||
|
||||
// Ensure report received by correct account.
|
||||
if report.TargetAccountID != receiving.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "receiver %s is not expected object %s", |
||||
receiving.URI, report.TargetAccount.URI) |
||||
} |
||||
|
||||
// Generate new ID for report.
|
||||
report.ID = id.NewULID() |
||||
|
||||
// Insert the new validated reported into the database.
|
||||
if err := f.state.DB.PutReport(ctx, report); err != nil { |
||||
return gtserror.Newf("error inserting %s into db: %w", report.URI, err) |
||||
} |
||||
|
||||
// Push message to worker queue to handle report side-effects.
|
||||
f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ |
||||
APObjectType: ap.ActivityFlag, |
||||
APActivityType: ap.ActivityCreate, |
||||
GTSModel: report, |
||||
Receiving: receiving, |
||||
Requesting: requesting, |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,84 @@
|
||||
// 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 federatingdb |
||||
|
||||
import ( |
||||
"context" |
||||
"net/http" |
||||
|
||||
"github.com/superseriousbusiness/activity/streams/vocab" |
||||
"github.com/superseriousbusiness/gotosocial/internal/ap" |
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror" |
||||
"github.com/superseriousbusiness/gotosocial/internal/id" |
||||
"github.com/superseriousbusiness/gotosocial/internal/log" |
||||
"github.com/superseriousbusiness/gotosocial/internal/messages" |
||||
) |
||||
|
||||
func (f *federatingDB) Follow(ctx context.Context, followable vocab.ActivityStreamsFollow) error { |
||||
log.DebugKV(ctx, "follow", serialize{followable}) |
||||
|
||||
// Mark activity as handled.
|
||||
f.storeActivityID(followable) |
||||
|
||||
// Extract relevant values from passed ctx.
|
||||
activityContext := getActivityContext(ctx) |
||||
if activityContext.internal { |
||||
return nil // Already processed.
|
||||
} |
||||
|
||||
requesting := activityContext.requestingAcct |
||||
receiving := activityContext.receivingAcct |
||||
|
||||
// Convert received AS block type to internal follow request model.
|
||||
followreq, err := f.converter.ASFollowToFollowRequest(ctx, followable) |
||||
if err != nil { |
||||
err := gtserror.Newf("error converting from AS type: %w", err) |
||||
return gtserror.WrapWithCode(http.StatusBadRequest, err) |
||||
} |
||||
|
||||
// Ensure follow enacted by correct account.
|
||||
if followreq.AccountID != requesting.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "requester %s is not expected actor %s", |
||||
requesting.URI, followreq.Account.URI) |
||||
} |
||||
|
||||
// Ensure follow received by correct account.
|
||||
if followreq.TargetAccountID != receiving.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "receiver %s is not expected object %s", |
||||
receiving.URI, followreq.TargetAccount.URI) |
||||
} |
||||
|
||||
// Generate new ID for followreq.
|
||||
followreq.ID = id.NewULID() |
||||
|
||||
// Insert the new validate follow request into the database.
|
||||
if err := f.state.DB.PutFollowRequest(ctx, followreq); err != nil { |
||||
return gtserror.Newf("error inserting %s into db: %w", followreq.URI, err) |
||||
} |
||||
|
||||
// Push message to worker queue to handle followreq side-effects.
|
||||
f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ |
||||
APObjectType: ap.ActivityFollow, |
||||
APActivityType: ap.ActivityCreate, |
||||
GTSModel: followreq, |
||||
Receiving: receiving, |
||||
Requesting: requesting, |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,147 @@
|
||||
// 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 federatingdb |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"net/http" |
||||
|
||||
"github.com/superseriousbusiness/activity/streams/vocab" |
||||
"github.com/superseriousbusiness/gotosocial/internal/ap" |
||||
"github.com/superseriousbusiness/gotosocial/internal/db" |
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror" |
||||
"github.com/superseriousbusiness/gotosocial/internal/id" |
||||
"github.com/superseriousbusiness/gotosocial/internal/log" |
||||
"github.com/superseriousbusiness/gotosocial/internal/messages" |
||||
) |
||||
|
||||
func (f *federatingDB) Like(ctx context.Context, likeable vocab.ActivityStreamsLike) error { |
||||
log.DebugKV(ctx, "like", serialize{likeable}) |
||||
|
||||
// Mark activity as handled.
|
||||
f.storeActivityID(likeable) |
||||
|
||||
// Extract relevant values from passed ctx.
|
||||
activityContext := getActivityContext(ctx) |
||||
if activityContext.internal { |
||||
return nil // Already processed.
|
||||
} |
||||
|
||||
requesting := activityContext.requestingAcct |
||||
receiving := activityContext.receivingAcct |
||||
|
||||
if receiving.IsMoving() { |
||||
// A Moving account
|
||||
// can't do this.
|
||||
return nil |
||||
} |
||||
|
||||
// Convert received AS like type to internal fave model.
|
||||
fave, err := f.converter.ASLikeToFave(ctx, likeable) |
||||
if err != nil { |
||||
err := gtserror.Newf("error converting from AS type: %w", err) |
||||
return gtserror.WrapWithCode(http.StatusBadRequest, err) |
||||
} |
||||
|
||||
// Ensure fave enacted by correct account.
|
||||
if fave.AccountID != requesting.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "requester %s is not expected actor %s", |
||||
requesting.URI, fave.Account.URI) |
||||
} |
||||
|
||||
// Ensure fave received by correct account.
|
||||
if fave.TargetAccountID != receiving.ID { |
||||
return gtserror.NewfWithCode(http.StatusForbidden, "receiver %s is not expected object %s", |
||||
receiving.URI, fave.TargetAccount.URI) |
||||
} |
||||
|
||||
if !*fave.Status.Local { |
||||
// Only process likes of local statuses.
|
||||
// TODO: process for remote statuses as well.
|
||||
return nil |
||||
} |
||||
|
||||
// Ensure valid Like target for requester.
|
||||
policyResult, err := f.intFilter.StatusLikeable(ctx, |
||||
requesting, |
||||
fave.Status, |
||||
) |
||||
if err != nil { |
||||
return gtserror.Newf("error seeing if status %s is likeable: %w", fave.Status.URI, err) |
||||
} |
||||
|
||||
if policyResult.Forbidden() { |
||||
return gtserror.NewWithCode(http.StatusForbidden, "requester does not have permission to Like status") |
||||
} |
||||
|
||||
// Derive pendingApproval
|
||||
// and preapproved status.
|
||||
var ( |
||||
pendingApproval bool |
||||
preApproved bool |
||||
) |
||||
|
||||
switch { |
||||
case policyResult.WithApproval(): |
||||
// Requester allowed to do
|
||||
// this pending approval.
|
||||
pendingApproval = true |
||||
|
||||
case policyResult.MatchedOnCollection(): |
||||
// Requester allowed to do this,
|
||||
// but matched on collection.
|
||||
// Preapprove Like and have the
|
||||
// processor send out an Accept.
|
||||
pendingApproval = true |
||||
preApproved = true |
||||
|
||||
case policyResult.Permitted(): |
||||
// Requester straight up
|
||||
// permitted to do this,
|
||||
// no need for Accept.
|
||||
pendingApproval = false |
||||
} |
||||
|
||||
// Set appropriate fields
|
||||
// on fave and store it.
|
||||
fave.ID = id.NewULID() |
||||
fave.PendingApproval = &pendingApproval |
||||
fave.PreApproved = preApproved |
||||
|
||||
if err := f.state.DB.PutStatusFave(ctx, fave); err != nil { |
||||
if errors.Is(err, db.ErrAlreadyExists) { |
||||
// The fave already exists in the
|
||||
// database, which means we've already
|
||||
// handled side effects. We can just
|
||||
// return nil here and be done with it.
|
||||
return nil |
||||
} |
||||
return gtserror.Newf("error inserting %s into db: %w", fave.URI, err) |
||||
} |
||||
|
||||
f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ |
||||
APObjectType: ap.ActivityLike, |
||||
APActivityType: ap.ActivityCreate, |
||||
GTSModel: fave, |
||||
Receiving: receiving, |
||||
Requesting: requesting, |
||||
}) |
||||
|
||||
return nil |
||||
} |
||||
Loading…
Reference in new issue