summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/federation/dereferencing/status_permitted.go235
1 files changed, 115 insertions, 120 deletions
diff --git a/internal/federation/dereferencing/status_permitted.go b/internal/federation/dereferencing/status_permitted.go
index 97cd61e93..9bd74811e 100644
--- a/internal/federation/dereferencing/status_permitted.go
+++ b/internal/federation/dereferencing/status_permitted.go
@@ -49,70 +49,67 @@ func (d *Dereferencer) isPermittedStatus(
existing *gtsmodel.Status,
status *gtsmodel.Status,
) (
- bool, // is permitted?
- error,
+ permitted bool, // is permitted?
+ err error,
) {
- // our failure condition handling
- // at the end of this function for
- // the case of permission = false.
- onFalse := func() (bool, error) {
- if existing != nil {
- log.Infof(ctx, "deleting unpermitted: %s", existing.URI)
-
- // Delete existing status from database as it's no longer permitted.
- if err := d.state.DB.DeleteStatusByID(ctx, existing.ID); err != nil {
- log.Errorf(ctx, "error deleting %s after permissivity fail: %v", existing.URI, err)
- }
- }
- return false, nil
- }
-
- if status.Account.IsSuspended() {
- // The status author is suspended,
- // this shouldn't have reached here
- // but it's a fast check anyways.
- log.Debugf(ctx,
- "status author %s is suspended",
- status.AccountURI,
- )
- return onFalse()
- }
-
- if inReplyTo := status.InReplyTo; inReplyTo != nil {
- return d.isPermittedReply(
- ctx,
+ switch {
+ case status.Account.IsSuspended():
+ // we shouldn't reach this point, log to poke devs to investigate.
+ log.Warnf(ctx, "status author suspended: %s", status.AccountURI)
+ permitted = false
+
+ case status.InReplyTo != nil:
+ // Status is a reply, check permissivity.
+ permitted, err = d.isPermittedReply(ctx,
requestUser,
status,
- inReplyTo,
- onFalse,
)
- } else if boostOf := status.BoostOf; boostOf != nil {
- return d.isPermittedBoost(
- ctx,
+ if err != nil {
+ return false, gtserror.Newf("error checking reply permissivity: %w", err)
+ }
+
+ case status.BoostOf != nil:
+ // Status is a boost, check permissivity.
+ permitted, err = d.isPermittedBoost(ctx,
requestUser,
status,
- boostOf,
- onFalse,
)
+ if err != nil {
+ return false, gtserror.Newf("error checking boost permissivity: %w", err)
+ }
+
+ default:
+ // In all other cases
+ // permit this status.
+ permitted = true
}
- // Nothing else stopping this.
- return true, nil
+ if !permitted && existing != nil {
+ log.Infof(ctx, "deleting unpermitted: %s", existing.URI)
+
+ // Delete existing status from database as it's no longer permitted.
+ if err := d.state.DB.DeleteStatusByID(ctx, existing.ID); err != nil {
+ log.Errorf(ctx, "error deleting %s after permissivity fail: %v", existing.URI, err)
+ }
+ }
+
+ return
}
func (d *Dereferencer) isPermittedReply(
ctx context.Context,
requestUser string,
status *gtsmodel.Status,
- inReplyTo *gtsmodel.Status,
- onFalse func() (bool, error),
) (bool, error) {
+ // Extract reply from status.
+ inReplyTo := status.InReplyTo
+
if inReplyTo.BoostOfID != "" {
// We do not permit replies to
// boost wrapper statuses. (this
// shouldn't be able to happen).
log.Info(ctx, "rejecting reply to boost wrapper status")
- return onFalse()
+ return false, nil
}
// Check visibility of local
@@ -130,7 +127,7 @@ func (d *Dereferencer) isPermittedReply(
// Our status is not visible to the
// account trying to do the reply.
if !visible {
- return onFalse()
+ return false, nil
}
}
@@ -147,7 +144,7 @@ func (d *Dereferencer) isPermittedReply(
if replyable.Forbidden() {
// Replier is not permitted
// to do this interaction.
- return onFalse()
+ return false, nil
}
if replyable.Permitted() &&
@@ -186,7 +183,7 @@ func (d *Dereferencer) isPermittedReply(
return true, nil
}
- return onFalse()
+ return false, nil
}
// Status claims to be approved, check
@@ -199,14 +196,12 @@ func (d *Dereferencer) isPermittedReply(
status.URI,
inReplyTo.AccountURI,
); err != nil {
+
// Error dereferencing means we couldn't
// get the Accept right now or it wasn't
// valid, so we shouldn't store this status.
- //
- // Do log the error though as it may be
- // interesting for admins to see.
- log.Info(ctx, "rejecting reply with undereferenceable ApprovedByURI: %v", err)
- return onFalse()
+ log.Errorf(ctx, "undereferencable ApprovedByURI: %v", err)
+ return false, nil
}
// Status has been approved.
@@ -218,15 +213,16 @@ func (d *Dereferencer) isPermittedBoost(
ctx context.Context,
requestUser string,
status *gtsmodel.Status,
- boostOf *gtsmodel.Status,
- onFalse func() (bool, error),
) (bool, error) {
+
+ // Extract boost from status.
+ boostOf := status.BoostOf
if boostOf.BoostOfID != "" {
+
// We do not permit boosts of
// boost wrapper statuses. (this
// shouldn't be able to happen).
- log.Info(ctx, "rejecting boost of boost wrapper status")
- return onFalse()
+ return false, nil
}
// Check visibility of local
@@ -244,7 +240,7 @@ func (d *Dereferencer) isPermittedBoost(
// Our status is not visible to the
// account trying to do the boost.
if !visible {
- return onFalse()
+ return false, nil
}
}
@@ -261,7 +257,7 @@ func (d *Dereferencer) isPermittedBoost(
if boostable.Forbidden() {
// Booster is not permitted
// to do this interaction.
- return onFalse()
+ return false, nil
}
if boostable.Permitted() &&
@@ -300,7 +296,7 @@ func (d *Dereferencer) isPermittedBoost(
return true, nil
}
- return onFalse()
+ return false, nil
}
// Boost claims to be approved, check
@@ -313,14 +309,12 @@ func (d *Dereferencer) isPermittedBoost(
status.URI,
boostOf.AccountURI,
); err != nil {
+
// Error dereferencing means we couldn't
// get the Accept right now or it wasn't
// valid, so we shouldn't store this status.
- //
- // Do log the error though as it may be
- // interesting for admins to see.
- log.Info(ctx, "rejecting boost with undereferenceable ApprovedByURI: %v", err)
- return onFalse()
+ log.Errorf(ctx, "undereferencable ApprovedByURI: %v", err)
+ return false, nil
}
// Status has been approved.
@@ -339,8 +333,8 @@ func (d *Dereferencer) validateApprovedBy(
ctx context.Context,
requestUser string,
approvedByURIStr string, // Eg., "https://example.org/users/someone/accepts/01J2736AWWJ3411CPR833F6D03"
- expectedObject string, // Eg., "https://some.instance.example.org/users/someone_else/statuses/01J27414TWV9F7DC39FN8ABB5R"
- expectedActor string, // Eg., "https://example.org/users/someone"
+ expectObjectURIStr string, // Eg., "https://some.instance.example.org/users/someone_else/statuses/01J27414TWV9F7DC39FN8ABB5R"
+ expectActorURIStr string, // Eg., "https://example.org/users/someone"
) error {
approvedByURI, err := url.Parse(approvedByURIStr)
if err != nil {
@@ -354,14 +348,14 @@ func (d *Dereferencer) validateApprovedBy(
return err
}
- transport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
+ tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
if err != nil {
err := gtserror.Newf("error creating transport: %w", err)
return err
}
// Make the call to resolve into an Acceptable.
- rsp, err := transport.Dereference(ctx, approvedByURI)
+ rsp, err := tsport.Dereference(ctx, approvedByURI)
if err != nil {
err := gtserror.Newf("error dereferencing %s: %w", approvedByURIStr, err)
return err
@@ -385,82 +379,83 @@ func (d *Dereferencer) validateApprovedBy(
// have changed (i.e. we followed some redirects).
rspURL := rsp.Request.URL
rspURLStr := rspURL.String()
- if rspURLStr != approvedByURIStr {
- // Final URI was different from approvedByURIStr.
- //
- // Make sure it's at least on the same host as
- // what we expected (ie., we weren't redirected
- // across domains), and make sure it's the same
- // as the ID of the Accept we were returned.
- if rspURL.Host != approvedByURI.Host {
- err := gtserror.Newf(
- "final dereference host %s did not match approvedByURI host %s",
- rspURL.Host, approvedByURI.Host,
- )
- return err
- }
+ switch {
+ case rspURLStr == approvedByURIStr:
- if acceptURIStr != rspURLStr {
- err := gtserror.Newf(
- "final dereference uri %s did not match returned Accept ID/URI %s",
- rspURLStr, acceptURIStr,
- )
- return err
- }
+ // i.e. from here, rspURLStr != approvedByURIStr.
+ //
+ // Make sure it's at least on the same host as
+ // what we expected (ie., we weren't redirected
+ // across domains), and make sure it's the same
+ // as the ID of the Accept we were returned.
+ case rspURL.Host != approvedByURI.Host:
+ return gtserror.Newf(
+ "final dereference host %s did not match approvedByURI host %s",
+ rspURL.Host, approvedByURI.Host,
+ )
+ case acceptURIStr != rspURLStr:
+ return gtserror.Newf(
+ "final dereference uri %s did not match returned Accept ID/URI %s",
+ rspURLStr, acceptURIStr,
+ )
}
- // Ensure the Accept URI has the same host
- // as the Accept Actor, so we know we're
- // not dealing with someone on a different
- // domain just pretending to be the Actor.
+ // Extract the actor IRI and string from Accept.
actorIRIs := ap.GetActorIRIs(acceptable)
- if len(actorIRIs) != 1 {
- err := gtserror.New("resolved Accept actor(s) length was not 1")
+ actorIRI, actorIRIStr := extractIRI(actorIRIs)
+ switch {
+ case actorIRIStr == "":
+ err := gtserror.New("missing Accept actor IRI")
return gtserror.SetMalformed(err)
- }
-
- actorIRI := actorIRIs[0]
- actorStr := actorIRI.String()
- if actorIRI.Host != acceptURI.Host {
- err := gtserror.Newf(
+ // Ensure the Accept Actor is who we expect
+ // it to be, and not someone else trying to
+ // do an Accept for an interaction with a
+ // statusable they don't own.
+ case actorIRI.Host != acceptURI.Host:
+ return gtserror.Newf(
"Accept Actor %s was not the same host as Accept %s",
- actorStr, acceptURIStr,
+ actorIRIStr, acceptURIStr,
)
- return err
- }
// Ensure the Accept Actor is who we expect
// it to be, and not someone else trying to
// do an Accept for an interaction with a
// statusable they don't own.
- if actorStr != expectedActor {
- err := gtserror.Newf(
+ case actorIRIStr != expectActorURIStr:
+ return gtserror.Newf(
"Accept Actor %s was not the same as expected actor %s",
- actorStr, expectedActor,
+ actorIRIStr, expectActorURIStr,
)
- return err
}
+ // Extract the object IRI string from Accept.
+ objectIRIs := ap.GetObjectIRIs(acceptable)
+ _, objectIRIStr := extractIRI(objectIRIs)
+ switch {
+ case objectIRIStr == "":
+ err := gtserror.New("missing Accept object IRI")
+ return gtserror.SetMalformed(err)
+
// Ensure the Accept Object is what we expect
// it to be, ie., it's Accepting the interaction
// we need it to Accept, and not something else.
- objectIRIs := ap.GetObjectIRIs(acceptable)
- if len(objectIRIs) != 1 {
- err := gtserror.New("resolved Accept object(s) length was not 1")
- return err
- }
-
- objectIRI := objectIRIs[0]
- objectStr := objectIRI.String()
-
- if objectStr != expectedObject {
- err := gtserror.Newf(
+ case objectIRIStr != expectObjectURIStr:
+ return gtserror.Newf(
"resolved Accept Object uri %s was not the same as expected object %s",
- objectStr, expectedObject,
+ objectIRIStr, expectObjectURIStr,
)
- return err
}
return nil
}
+
+// extractIRI is shorthand to extract the first IRI
+// url.URL{} object and serialized form from slice.
+func extractIRI(iris []*url.URL) (*url.URL, string) {
+ if len(iris) == 0 {
+ return nil, ""
+ }
+ u := iris[0]
+ return u, u.String()
+}