summaryrefslogtreecommitdiff
path: root/internal/typeutils
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-07-11 16:22:21 +0200
committerLibravatar GitHub <noreply@github.com>2021-07-11 16:22:21 +0200
commit846057f0d696fded87d105dec1245e9ba32763ce (patch)
tree9a4914c07bcf189a3eea0a2c091567c56cdf4963 /internal/typeutils
parentfavourites GET implementation (#95) (diff)
downloadgotosocial-846057f0d696fded87d105dec1245e9ba32763ce.tar.xz
Block/unblock (#96)
* remote + local block logic, incl. federation * improve blocking stuff * fiddle with display of blocked profiles * go fmt
Diffstat (limited to 'internal/typeutils')
-rw-r--r--internal/typeutils/asinterfaces.go9
-rw-r--r--internal/typeutils/astointernal.go35
-rw-r--r--internal/typeutils/converter.go13
-rw-r--r--internal/typeutils/internal.go1
-rw-r--r--internal/typeutils/internaltoas.go70
-rw-r--r--internal/typeutils/internaltofrontend.go33
6 files changed, 161 insertions, 0 deletions
diff --git a/internal/typeutils/asinterfaces.go b/internal/typeutils/asinterfaces.go
index aae3ecf93..d0b1cf617 100644
--- a/internal/typeutils/asinterfaces.go
+++ b/internal/typeutils/asinterfaces.go
@@ -111,6 +111,15 @@ type Likeable interface {
withObject
}
+// Blockable represents the minimum interface for an activitystreams 'block' activity.
+type Blockable interface {
+ withJSONLDId
+ withTypeName
+
+ withActor
+ withObject
+}
+
// Announceable represents the minimum interface for an activitystreams 'announce' activity.
type Announceable interface {
withJSONLDId
diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go
index dc58346fb..394de6e82 100644
--- a/internal/typeutils/astointernal.go
+++ b/internal/typeutils/astointernal.go
@@ -426,6 +426,41 @@ func (c *converter) ASLikeToFave(likeable Likeable) (*gtsmodel.StatusFave, error
}, nil
}
+func (c *converter) ASBlockToBlock(blockable Blockable) (*gtsmodel.Block, error) {
+ idProp := blockable.GetJSONLDId()
+ if idProp == nil || !idProp.IsIRI() {
+ return nil, errors.New("ASBlockToBlock: no id property set on block, or was not an iri")
+ }
+ uri := idProp.GetIRI().String()
+
+ origin, err := extractActor(blockable)
+ if err != nil {
+ return nil, errors.New("ASBlockToBlock: error extracting actor property from block")
+ }
+ originAccount := &gtsmodel.Account{}
+ if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: origin.String()}}, originAccount); err != nil {
+ return nil, fmt.Errorf("ASBlockToBlock: error extracting account with uri %s from the database: %s", origin.String(), err)
+ }
+
+ target, err := extractObject(blockable)
+ if err != nil {
+ return nil, errors.New("ASBlockToBlock: error extracting object property from block")
+ }
+
+ targetAccount := &gtsmodel.Account{}
+ if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: target.String(), CaseInsensitive: true}}, targetAccount); err != nil {
+ return nil, fmt.Errorf("ASBlockToBlock: error extracting account with uri %s from the database: %s", target.String(), err)
+ }
+
+ return &gtsmodel.Block{
+ AccountID: originAccount.ID,
+ Account: originAccount,
+ TargetAccountID: targetAccount.ID,
+ TargetAccount: targetAccount,
+ URI: uri,
+ }, nil
+}
+
func (c *converter) ASAnnounceToStatus(announceable Announceable) (*gtsmodel.Status, bool, error) {
status := &gtsmodel.Status{}
isNew := true
diff --git a/internal/typeutils/converter.go b/internal/typeutils/converter.go
index 30c1c7d2c..57c2a1f6d 100644
--- a/internal/typeutils/converter.go
+++ b/internal/typeutils/converter.go
@@ -48,6 +48,10 @@ type TypeConverter interface {
// if something goes wrong. The returned account should be ready to serialize on an API level, and may NOT have sensitive fields.
// In other words, this is the public record that the server has of an account.
AccountToMastoPublic(account *gtsmodel.Account) (*model.Account, error)
+ // AccountToMastoBlocked takes a db model account as a param, and returns a mastotype account, or an error if
+ // something goes wrong. The returned account will be a bare minimum representation of the account. This function should be used
+ // when someone wants to view an account they've blocked.
+ AccountToMastoBlocked(account *gtsmodel.Account) (*model.Account, error)
// AppToMastoSensitive takes a db model application as a param, and returns a populated mastotype application, or an error
// if something goes wrong. The returned application should be ready to serialize on an API level, and may have sensitive fields
// (such as client id and client secret), so serve it only to an authorized user who should have permission to see it.
@@ -104,6 +108,8 @@ type TypeConverter interface {
ASFollowToFollow(followable Followable) (*gtsmodel.Follow, error)
// ASLikeToFave converts a remote activitystreams 'like' representation into a gts model status fave.
ASLikeToFave(likeable Likeable) (*gtsmodel.StatusFave, error)
+ // ASBlockToBlock converts a remote activity streams 'block' representation into a gts model block.
+ ASBlockToBlock(blockable Blockable) (*gtsmodel.Block, error)
// ASAnnounceToStatus converts an activitystreams 'announce' into a status.
//
// The returned bool indicates whether this status is new (true) or not new (false).
@@ -124,6 +130,11 @@ type TypeConverter interface {
// AccountToAS converts a gts model account into an activity streams person, suitable for federation
AccountToAS(a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error)
+ // AccountToASMinimal converts a gts model account into an activity streams person, suitable for federation.
+ //
+ // The returned account will just have the Type, Username, PublicKey, and ID properties set. This is
+ // suitable for serving to requesters to whom we want to give as little information as possible because
+ // we don't trust them (yet).
AccountToASMinimal(a *gtsmodel.Account) (vocab.ActivityStreamsPerson, error)
// StatusToAS converts a gts model status into an activity streams note, suitable for federation
StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, error)
@@ -137,6 +148,8 @@ type TypeConverter interface {
FaveToAS(f *gtsmodel.StatusFave) (vocab.ActivityStreamsLike, error)
// BoostToAS converts a gts model boost into an activityStreams ANNOUNCE, suitable for federation
BoostToAS(boostWrapperStatus *gtsmodel.Status, boostingAccount *gtsmodel.Account, boostedAccount *gtsmodel.Account) (vocab.ActivityStreamsAnnounce, error)
+ // BlockToAS converts a gts model block into an activityStreams BLOCK, suitable for federation.
+ BlockToAS(block *gtsmodel.Block) (vocab.ActivityStreamsBlock, error)
/*
INTERNAL (gts) MODEL TO INTERNAL MODEL
diff --git a/internal/typeutils/internal.go b/internal/typeutils/internal.go
index b081708a2..a46ad7fbd 100644
--- a/internal/typeutils/internal.go
+++ b/internal/typeutils/internal.go
@@ -67,6 +67,7 @@ func (c *converter) StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.
Language: s.Language,
Text: s.Text,
BoostOfID: s.ID,
+ BoostOfAccountID: s.AccountID,
Visibility: s.Visibility,
VisibilityAdvanced: s.VisibilityAdvanced,
diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go
index 1760b8731..809cedc45 100644
--- a/internal/typeutils/internaltoas.go
+++ b/internal/typeutils/internaltoas.go
@@ -780,3 +780,73 @@ func (c *converter) BoostToAS(boostWrapperStatus *gtsmodel.Status, boostingAccou
return announce, nil
}
+
+/*
+ we want to end up with something like this:
+
+ {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "actor": "https://example.org/users/some_user",
+ "id":"https://example.org/users/some_user/blocks/SOME_ULID_OF_A_BLOCK",
+ "object":"https://some_other.instance/users/some_other_user",
+ "type":"Block"
+ }
+*/
+func (c *converter) BlockToAS(b *gtsmodel.Block) (vocab.ActivityStreamsBlock, error) {
+ if b.Account == nil {
+ a := &gtsmodel.Account{}
+ if err := c.db.GetByID(b.AccountID, a); err != nil {
+ return nil, fmt.Errorf("BlockToAS: error getting block account from database: %s", err)
+ }
+ b.Account = a
+ }
+
+ if b.TargetAccount == nil {
+ a := &gtsmodel.Account{}
+ if err := c.db.GetByID(b.TargetAccountID, a); err != nil {
+ return nil, fmt.Errorf("BlockToAS: error getting block target account from database: %s", err)
+ }
+ b.TargetAccount = a
+ }
+
+ // create the block
+ block := streams.NewActivityStreamsBlock()
+
+ // set the actor property to the block-ing account's URI
+ actorProp := streams.NewActivityStreamsActorProperty()
+ actorIRI, err := url.Parse(b.Account.URI)
+ if err != nil {
+ return nil, fmt.Errorf("BlockToAS: error parsing uri %s: %s", b.Account.URI, err)
+ }
+ actorProp.AppendIRI(actorIRI)
+ block.SetActivityStreamsActor(actorProp)
+
+ // set the ID property to the blocks's URI
+ idProp := streams.NewJSONLDIdProperty()
+ idIRI, err := url.Parse(b.URI)
+ if err != nil {
+ return nil, fmt.Errorf("BlockToAS: error parsing uri %s: %s", b.URI, err)
+ }
+ idProp.Set(idIRI)
+ block.SetJSONLDId(idProp)
+
+ // set the object property to the target account's URI
+ objectProp := streams.NewActivityStreamsObjectProperty()
+ targetIRI, err := url.Parse(b.TargetAccount.URI)
+ if err != nil {
+ return nil, fmt.Errorf("BlockToAS: error parsing uri %s: %s", b.TargetAccount.URI, err)
+ }
+ objectProp.AppendIRI(targetIRI)
+ block.SetActivityStreamsObject(objectProp)
+
+ // set the TO property to the target account's IRI
+ toProp := streams.NewActivityStreamsToProperty()
+ toIRI, err := url.Parse(b.TargetAccount.URI)
+ if err != nil {
+ return nil, fmt.Errorf("BlockToAS: error parsing uri %s: %s", b.TargetAccount.URI, err)
+ }
+ toProp.AppendIRI(toIRI)
+ block.SetActivityStreamsTo(toProp)
+
+ return block, nil
+}
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index 61c11b8ef..03e071981 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -150,6 +150,11 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*model.Account, e
acct = a.Username
}
+ var suspended bool
+ if !a.SuspendedAt.IsZero() {
+ suspended = true
+ }
+
return &model.Account{
ID: a.ID,
Username: a.Username,
@@ -170,6 +175,34 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*model.Account, e
LastStatusAt: lastStatusAt,
Emojis: emojis, // TODO: implement this
Fields: fields,
+ Suspended: suspended,
+ }, nil
+}
+
+func (c *converter) AccountToMastoBlocked(a *gtsmodel.Account) (*model.Account, error) {
+ var acct string
+ if a.Domain != "" {
+ // this is a remote user
+ acct = fmt.Sprintf("%s@%s", a.Username, a.Domain)
+ } else {
+ // this is a local user
+ acct = a.Username
+ }
+
+ var suspended bool
+ if !a.SuspendedAt.IsZero() {
+ suspended = true
+ }
+
+ return &model.Account{
+ ID: a.ID,
+ Username: a.Username,
+ Acct: acct,
+ DisplayName: a.DisplayName,
+ Bot: a.Bot,
+ CreatedAt: a.CreatedAt.Format(time.RFC3339),
+ URL: a.URL,
+ Suspended: suspended,
}, nil
}