mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-11 02:47:29 -06:00
parse various types of actor
This commit is contained in:
parent
8104a03bd5
commit
064f961008
11 changed files with 264 additions and 110 deletions
|
|
@ -145,7 +145,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
||||||
|
|
||||||
// convert person to account
|
// convert person to account
|
||||||
// since this account is already known, we should get a pretty full model of it from the conversion
|
// since this account is already known, we should get a pretty full model of it from the conversion
|
||||||
a, err := suite.tc.ASPersonToAccount(person)
|
a, err := suite.tc.ASRepresentationToAccount(person)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
assert.EqualValues(suite.T(), targetAccount.Username, a.Username)
|
assert.EqualValues(suite.T(), targetAccount.Username, a.Username)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
|
||||||
return ctx, false, fmt.Errorf("error dereferencing account with public key id %s: %s", publicKeyOwnerURI.String(), err)
|
return ctx, false, fmt.Errorf("error dereferencing account with public key id %s: %s", publicKeyOwnerURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := f.typeConverter.ASPersonToAccount(person)
|
a, err := f.typeConverter.ASRepresentationToAccount(person)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctx, false, fmt.Errorf("error converting person with public key id %s to account: %s", publicKeyOwnerURI.String(), err)
|
return ctx, false, fmt.Errorf("error converting person with public key id %s to account: %s", publicKeyOwnerURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/go-fed/activity/pub"
|
"github.com/go-fed/activity/pub"
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
|
@ -38,9 +37,9 @@ type Federator interface {
|
||||||
// AuthenticateFederatedRequest can be used to check the authenticity of incoming http-signed requests for federating resources.
|
// AuthenticateFederatedRequest can be used to check the authenticity of incoming http-signed requests for federating resources.
|
||||||
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
||||||
AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, error)
|
AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, error)
|
||||||
// DereferenceRemoteAccount can be used to get the ActivityStreamsPerson representation of a remote account, based on the account ID (which is a URI).
|
// DereferenceRemoteAccount can be used to get the representation of a remote account, based on the account ID (which is a URI).
|
||||||
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
||||||
DereferenceRemoteAccount(username string, remoteAccountID *url.URL) (vocab.ActivityStreamsPerson, error)
|
DereferenceRemoteAccount(username string, remoteAccountID *url.URL) (typeutils.Accountable, error)
|
||||||
// GetTransportForUser returns a new transport initialized with the key credentials belonging to the given username.
|
// GetTransportForUser returns a new transport initialized with the key credentials belonging to the given username.
|
||||||
// This can be used for making signed http requests.
|
// This can be used for making signed http requests.
|
||||||
GetTransportForUser(username string) (pub.Transport, error)
|
GetTransportForUser(username string) (pub.Transport, error)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/go-fed/httpsig"
|
"github.com/go-fed/httpsig"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -177,7 +178,7 @@ func (f *federator) AuthenticateFederatedRequest(username string, r *http.Reques
|
||||||
return pkOwnerURI, nil
|
return pkOwnerURI, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federator) DereferenceRemoteAccount(username string, remoteAccountID *url.URL) (vocab.ActivityStreamsPerson, error) {
|
func (f *federator) DereferenceRemoteAccount(username string, remoteAccountID *url.URL) (typeutils.Accountable, error) {
|
||||||
|
|
||||||
transport, err := f.GetTransportForUser(username)
|
transport, err := f.GetTransportForUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func (p *processor) authenticateAndDereferenceFediRequest(username string, r *ht
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert it to our internal account representation
|
// convert it to our internal account representation
|
||||||
requestingAccount, err = p.tc.ASPersonToAccount(requestingPerson)
|
requestingAccount, err = p.tc.ASRepresentationToAccount(requestingPerson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't convert dereferenced uri %s to gtsmodel account: %s", requestingAccountURI.String(), err)
|
return nil, fmt.Errorf("couldn't convert dereferenced uri %s to gtsmodel account: %s", requestingAccountURI.String(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,30 +19,100 @@
|
||||||
package typeutils
|
package typeutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/go-fed/activity/pub"
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type usernameable interface {
|
// Accountable represents the minimum activitypub interface for representing an 'account'.
|
||||||
|
// This interface is fulfilled by, for example, vocab.ActivityStreamsPerson and vocab.ActivityStreamsApplication
|
||||||
|
type Accountable interface {
|
||||||
|
withJSONLDId
|
||||||
|
withGetTypeName
|
||||||
|
withPreferredUsername
|
||||||
|
withIcon
|
||||||
|
withDisplayName
|
||||||
|
withImage
|
||||||
|
withSummary
|
||||||
|
withDiscoverable
|
||||||
|
withURL
|
||||||
|
withPublicKey
|
||||||
|
withInbox
|
||||||
|
withOutbox
|
||||||
|
withFollowing
|
||||||
|
withFollowers
|
||||||
|
withFeatured
|
||||||
|
}
|
||||||
|
|
||||||
|
// all the interfaces below narrow down one particular field of an activity streams object for easy extraction
|
||||||
|
|
||||||
|
type withJSONLDId interface {
|
||||||
|
GetJSONLDId() vocab.JSONLDIdProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withGetTypeName interface {
|
||||||
|
GetTypeName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type withPreferredUsername interface {
|
||||||
GetActivityStreamsPreferredUsername() vocab.ActivityStreamsPreferredUsernameProperty
|
GetActivityStreamsPreferredUsername() vocab.ActivityStreamsPreferredUsernameProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
type iconable interface {
|
type withIcon interface {
|
||||||
GetActivityStreamsIcon() vocab.ActivityStreamsIconProperty
|
GetActivityStreamsIcon() vocab.ActivityStreamsIconProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
type displaynameable interface {
|
type withDisplayName interface {
|
||||||
GetActivityStreamsName() vocab.ActivityStreamsNameProperty
|
GetActivityStreamsName() vocab.ActivityStreamsNameProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
type imageable interface {
|
type withImage interface {
|
||||||
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
GetActivityStreamsImage() vocab.ActivityStreamsImageProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractPreferredUsername(i usernameable) (string, error) {
|
type withSummary interface {
|
||||||
|
GetActivityStreamsSummary() vocab.ActivityStreamsSummaryProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withDiscoverable interface {
|
||||||
|
GetTootDiscoverable() vocab.TootDiscoverableProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withURL interface {
|
||||||
|
GetActivityStreamsUrl() vocab.ActivityStreamsUrlProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withPublicKey interface {
|
||||||
|
GetW3IDSecurityV1PublicKey() vocab.W3IDSecurityV1PublicKeyProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withInbox interface {
|
||||||
|
GetActivityStreamsInbox() vocab.ActivityStreamsInboxProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withOutbox interface {
|
||||||
|
GetActivityStreamsOutbox() vocab.ActivityStreamsOutboxProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withFollowing interface {
|
||||||
|
GetActivityStreamsFollowing() vocab.ActivityStreamsFollowingProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withFollowers interface {
|
||||||
|
GetActivityStreamsFollowers() vocab.ActivityStreamsFollowersProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type withFeatured interface {
|
||||||
|
GetTootFeatured() vocab.TootFeaturedProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractPreferredUsername(i withPreferredUsername) (string, error) {
|
||||||
u := i.GetActivityStreamsPreferredUsername()
|
u := i.GetActivityStreamsPreferredUsername()
|
||||||
if u == nil || !u.IsXMLSchemaString() {
|
if u == nil || !u.IsXMLSchemaString() {
|
||||||
return "", errors.New("preferredUsername was not a string")
|
return "", errors.New("preferredUsername was not a string")
|
||||||
|
|
@ -53,7 +123,7 @@ func extractPreferredUsername(i usernameable) (string, error) {
|
||||||
return u.GetXMLSchemaString(), nil
|
return u.GetXMLSchemaString(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractName(i displaynameable) (string, error) {
|
func extractName(i withDisplayName) (string, error) {
|
||||||
nameProp := i.GetActivityStreamsName()
|
nameProp := i.GetActivityStreamsName()
|
||||||
if nameProp == nil {
|
if nameProp == nil {
|
||||||
return "", errors.New("activityStreamsName not found")
|
return "", errors.New("activityStreamsName not found")
|
||||||
|
|
@ -75,7 +145,7 @@ func extractName(i displaynameable) (string, error) {
|
||||||
// "type": "Image",
|
// "type": "Image",
|
||||||
// "url": "http://example.org/path/to/some/file.jpeg"
|
// "url": "http://example.org/path/to/some/file.jpeg"
|
||||||
// },
|
// },
|
||||||
func extractIconURL(i iconable) (*url.URL, error) {
|
func extractIconURL(i withIcon) (*url.URL, error) {
|
||||||
iconProp := i.GetActivityStreamsIcon()
|
iconProp := i.GetActivityStreamsIcon()
|
||||||
if iconProp == nil {
|
if iconProp == nil {
|
||||||
return nil, errors.New("icon property was nil")
|
return nil, errors.New("icon property was nil")
|
||||||
|
|
@ -84,8 +154,7 @@ func extractIconURL(i iconable) (*url.URL, error) {
|
||||||
// icon can potentially contain multiple entries, so we iterate through all of them
|
// icon can potentially contain multiple entries, so we iterate through all of them
|
||||||
// here in order to find the first one that meets these criteria:
|
// here in order to find the first one that meets these criteria:
|
||||||
// 1. is an image
|
// 1. is an image
|
||||||
// 2. is a supported type
|
// 2. has a URL so we can grab it
|
||||||
// 3. has a URL so we can grab it
|
|
||||||
for iconIter := iconProp.Begin(); iconIter != iconProp.End(); iconIter = iconIter.Next() {
|
for iconIter := iconProp.Begin(); iconIter != iconProp.End(); iconIter = iconIter.Next() {
|
||||||
// 1. is an image
|
// 1. is an image
|
||||||
if !iconIter.IsActivityStreamsImage() {
|
if !iconIter.IsActivityStreamsImage() {
|
||||||
|
|
@ -96,43 +165,23 @@ func extractIconURL(i iconable) (*url.URL, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. is a supported type
|
// 2. has a URL so we can grab it
|
||||||
imageType := imageValue.GetActivityStreamsMediaType()
|
url, err := extractURL(imageValue)
|
||||||
if imageType == nil || !media.SupportedImageType(imageType.Get()) {
|
if err == nil && url != nil {
|
||||||
continue
|
return url, nil
|
||||||
}
|
|
||||||
|
|
||||||
// 3. has a URL so we can grab it
|
|
||||||
imageURLProp := imageValue.GetActivityStreamsUrl()
|
|
||||||
if imageURLProp == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL is also an iterable!
|
|
||||||
// so let's take the first valid one we can find
|
|
||||||
for urlIter := imageURLProp.Begin(); urlIter != imageURLProp.End(); urlIter = urlIter.Next() {
|
|
||||||
if !urlIter.IsIRI() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if urlIter.GetIRI() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// found it!!!
|
|
||||||
return urlIter.GetIRI(), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we get to this point we didn't find an icon meeting our criteria :'(
|
// if we get to this point we didn't find an icon meeting our criteria :'(
|
||||||
return nil, errors.New("could not extract valid image from icon")
|
return nil, errors.New("could not extract valid image from icon")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// extractImageURL extracts a URL to a supported image file from something like:
|
// extractImageURL extracts a URL to a supported image file from something like:
|
||||||
// "image": {
|
// "image": {
|
||||||
// "mediaType": "image/jpeg",
|
// "mediaType": "image/jpeg",
|
||||||
// "type": "Image",
|
// "type": "Image",
|
||||||
// "url": "http://example.org/path/to/some/file.jpeg"
|
// "url": "http://example.org/path/to/some/file.jpeg"
|
||||||
// },
|
// },
|
||||||
func extractImageURL(i imageable) (*url.URL, error) {
|
func extractImageURL(i withImage) (*url.URL, error) {
|
||||||
imageProp := i.GetActivityStreamsImage()
|
imageProp := i.GetActivityStreamsImage()
|
||||||
if imageProp == nil {
|
if imageProp == nil {
|
||||||
return nil, errors.New("icon property was nil")
|
return nil, errors.New("icon property was nil")
|
||||||
|
|
@ -141,8 +190,7 @@ func extractImageURL(i imageable) (*url.URL, error) {
|
||||||
// icon can potentially contain multiple entries, so we iterate through all of them
|
// icon can potentially contain multiple entries, so we iterate through all of them
|
||||||
// here in order to find the first one that meets these criteria:
|
// here in order to find the first one that meets these criteria:
|
||||||
// 1. is an image
|
// 1. is an image
|
||||||
// 2. is a supported type
|
// 2. has a URL so we can grab it
|
||||||
// 3. has a URL so we can grab it
|
|
||||||
for imageIter := imageProp.Begin(); imageIter != imageProp.End(); imageIter = imageIter.Next() {
|
for imageIter := imageProp.Begin(); imageIter != imageProp.End(); imageIter = imageIter.Next() {
|
||||||
// 1. is an image
|
// 1. is an image
|
||||||
if !imageIter.IsActivityStreamsImage() {
|
if !imageIter.IsActivityStreamsImage() {
|
||||||
|
|
@ -153,31 +201,99 @@ func extractImageURL(i imageable) (*url.URL, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. is a supported type
|
// 2. has a URL so we can grab it
|
||||||
imageType := imageValue.GetActivityStreamsMediaType()
|
url, err := extractURL(imageValue)
|
||||||
if imageType == nil || !media.SupportedImageType(imageType.Get()) {
|
if err == nil && url != nil {
|
||||||
continue
|
return url, nil
|
||||||
}
|
|
||||||
|
|
||||||
// 3. has a URL so we can grab it
|
|
||||||
imageURLProp := imageValue.GetActivityStreamsUrl()
|
|
||||||
if imageURLProp == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// URL is also an iterable!
|
|
||||||
// so let's take the first valid one we can find
|
|
||||||
for urlIter := imageURLProp.Begin(); urlIter != imageURLProp.End(); urlIter = urlIter.Next() {
|
|
||||||
if !urlIter.IsIRI() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if urlIter.GetIRI() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// found it!!!
|
|
||||||
return urlIter.GetIRI(), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we get to this point we didn't find an image meeting our criteria :'(
|
// if we get to this point we didn't find an image meeting our criteria :'(
|
||||||
return nil, errors.New("could not extract valid image from image property")
|
return nil, errors.New("could not extract valid image from image property")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractSummary(i withSummary) (string, error) {
|
||||||
|
summaryProp := i.GetActivityStreamsSummary()
|
||||||
|
if summaryProp == nil {
|
||||||
|
return "", errors.New("summary property was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
for summaryIter := summaryProp.Begin(); summaryIter != summaryProp.End(); summaryIter = summaryIter.Next() {
|
||||||
|
if summaryIter.IsXMLSchemaString() && summaryIter.GetXMLSchemaString() != "" {
|
||||||
|
return summaryIter.GetXMLSchemaString(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("could not extract summary")
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractDiscoverable(i withDiscoverable) (bool, error) {
|
||||||
|
if i.GetTootDiscoverable() == nil {
|
||||||
|
return false, errors.New("discoverable was nil")
|
||||||
|
}
|
||||||
|
return i.GetTootDiscoverable().Get(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractURL(i withURL) (*url.URL, error) {
|
||||||
|
urlProp := i.GetActivityStreamsUrl()
|
||||||
|
if urlProp == nil {
|
||||||
|
return nil, errors.New("url property was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
for urlIter := urlProp.Begin(); urlIter != urlProp.End(); urlIter = urlIter.Next() {
|
||||||
|
if urlIter.IsIRI() && urlIter.GetIRI() != nil {
|
||||||
|
return urlIter.GetIRI(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("could not extract url")
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractPublicKeyForOwner(i withPublicKey, forOwner *url.URL) (*rsa.PublicKey, *url.URL, error) {
|
||||||
|
publicKeyProp := i.GetW3IDSecurityV1PublicKey()
|
||||||
|
if publicKeyProp == nil {
|
||||||
|
return nil, nil, errors.New("public key property was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
for publicKeyIter := publicKeyProp.Begin(); publicKeyIter != publicKeyProp.End(); publicKeyIter = publicKeyIter.Next() {
|
||||||
|
pkey := publicKeyIter.Get()
|
||||||
|
if pkey == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkeyID, err := pub.GetId(pkey)
|
||||||
|
if err != nil || pkeyID == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkey.GetW3IDSecurityV1Owner() == nil || pkey.GetW3IDSecurityV1Owner().Get() == nil || pkey.GetW3IDSecurityV1Owner().Get().String() != forOwner.String() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkey.GetW3IDSecurityV1PublicKeyPem() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkeyPem := pkey.GetW3IDSecurityV1PublicKeyPem().Get()
|
||||||
|
if pkeyPem == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(pkeyPem))
|
||||||
|
if block == nil || block.Type != "PUBLIC KEY" {
|
||||||
|
return nil, nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("could not parse public key from block bytes: %s", err)
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return nil, nil, errors.New("returned public key was empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if publicKey, ok := p.(*rsa.PublicKey); ok {
|
||||||
|
return publicKey, pkeyID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("couldn't find public key")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,13 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *converter) ASPersonToAccount(person vocab.ActivityStreamsPerson) (*gtsmodel.Account, error) {
|
func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmodel.Account, error) {
|
||||||
// first check if we actually already know this person
|
// first check if we actually already know this account
|
||||||
uriProp := person.GetJSONLDId()
|
uriProp := accountable.GetJSONLDId()
|
||||||
if uriProp == nil || !uriProp.IsIRI() {
|
if uriProp == nil || !uriProp.IsIRI() {
|
||||||
return nil, errors.New("no id property found on person, or id was not an iri")
|
return nil, errors.New("no id property found on person, or id was not an iri")
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +51,7 @@ func (c *converter) ASPersonToAccount(person vocab.ActivityStreamsPerson) (*gtsm
|
||||||
|
|
||||||
// Username aka preferredUsername
|
// Username aka preferredUsername
|
||||||
// We need this one so bail if it's not set.
|
// We need this one so bail if it's not set.
|
||||||
username, err := extractPreferredUsername(person)
|
username, err := extractPreferredUsername(accountable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't extract username: %s", err)
|
return nil, fmt.Errorf("couldn't extract username: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -63,64 +62,103 @@ func (c *converter) ASPersonToAccount(person vocab.ActivityStreamsPerson) (*gtsm
|
||||||
|
|
||||||
// avatar aka icon
|
// avatar aka icon
|
||||||
// if this one isn't extractable in a format we recognise we'll just skip it
|
// if this one isn't extractable in a format we recognise we'll just skip it
|
||||||
if avatarURL, err := extractIconURL(person); err == nil {
|
if avatarURL, err := extractIconURL(accountable); err == nil {
|
||||||
acct.AvatarRemoteURL = avatarURL.String()
|
acct.AvatarRemoteURL = avatarURL.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// header aka image
|
// header aka image
|
||||||
// if this one isn't extractable in a format we recognise we'll just skip it
|
// if this one isn't extractable in a format we recognise we'll just skip it
|
||||||
if headerURL, err := extractImageURL(person); err == nil {
|
if headerURL, err := extractImageURL(accountable); err == nil {
|
||||||
acct.HeaderRemoteURL = headerURL.String()
|
acct.HeaderRemoteURL = headerURL.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// display name aka name
|
// display name aka name
|
||||||
// we default to the username, but take the more nuanced name property if it exists
|
// we default to the username, but take the more nuanced name property if it exists
|
||||||
acct.DisplayName = username
|
acct.DisplayName = username
|
||||||
if displayName, err := extractName(person); err == nil {
|
if displayName, err := extractName(accountable); err == nil {
|
||||||
acct.DisplayName = displayName
|
acct.DisplayName = displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields aka attachment array
|
// TODO: fields aka attachment array
|
||||||
// TODO
|
|
||||||
|
|
||||||
// note aka summary
|
// note aka summary
|
||||||
// TODO
|
note, err := extractSummary(accountable)
|
||||||
|
if err == nil && note != "" {
|
||||||
|
acct.Note = note
|
||||||
|
}
|
||||||
|
|
||||||
// bot
|
// check for bot and actor type
|
||||||
// TODO: parse this from application vs. person type
|
switch gtsmodel.ActivityStreamsActor(accountable.GetTypeName()) {
|
||||||
|
case gtsmodel.ActivityStreamsPerson, gtsmodel.ActivityStreamsGroup, gtsmodel.ActivityStreamsOrganization:
|
||||||
|
// people, groups, and organizations aren't bots
|
||||||
|
acct.Bot = false
|
||||||
|
// apps and services are
|
||||||
|
case gtsmodel.ActivityStreamsApplication, gtsmodel.ActivityStreamsService:
|
||||||
|
acct.Bot = true
|
||||||
|
default:
|
||||||
|
// we don't know what this is!
|
||||||
|
return nil, fmt.Errorf("type name %s not recognised or not convertible to gtsmodel.ActivityStreamsActor", accountable.GetTypeName())
|
||||||
|
}
|
||||||
|
acct.ActorType = gtsmodel.ActivityStreamsActor(accountable.GetTypeName())
|
||||||
|
|
||||||
// locked aka manuallyApprovesFollowers
|
// TODO: locked aka manuallyApprovesFollowers
|
||||||
// TODO
|
|
||||||
|
|
||||||
// discoverable
|
// discoverable
|
||||||
// TODO
|
// default to false -- take custom value if it's set though
|
||||||
|
acct.Discoverable = false
|
||||||
|
discoverable, err := extractDiscoverable(accountable)
|
||||||
|
if err == nil {
|
||||||
|
acct.Discoverable = discoverable
|
||||||
|
}
|
||||||
|
|
||||||
// url property
|
// url property
|
||||||
// TODO
|
url, err := extractURL(accountable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not extract url for person with id %s: %s", uri.String(), err)
|
||||||
|
}
|
||||||
|
acct.URL = url.String()
|
||||||
|
|
||||||
// InboxURI
|
// InboxURI
|
||||||
// TODO
|
if accountable.GetActivityStreamsInbox() == nil || accountable.GetActivityStreamsInbox().GetIRI() == nil {
|
||||||
|
return nil, fmt.Errorf("person with id %s had no inbox uri", uri.String())
|
||||||
|
}
|
||||||
|
acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String()
|
||||||
|
|
||||||
// OutboxURI
|
// OutboxURI
|
||||||
// TODO
|
if accountable.GetActivityStreamsOutbox() == nil || accountable.GetActivityStreamsOutbox().GetIRI() == nil {
|
||||||
|
return nil, fmt.Errorf("person with id %s had no outbox uri", uri.String())
|
||||||
|
}
|
||||||
|
acct.OutboxURI = accountable.GetActivityStreamsOutbox().GetIRI().String()
|
||||||
|
|
||||||
// FollowingURI
|
// FollowingURI
|
||||||
// TODO
|
if accountable.GetActivityStreamsFollowing() == nil || accountable.GetActivityStreamsFollowing().GetIRI() == nil {
|
||||||
|
return nil, fmt.Errorf("person with id %s had no following uri", uri.String())
|
||||||
|
}
|
||||||
|
acct.FollowingURI = accountable.GetActivityStreamsFollowing().GetIRI().String()
|
||||||
|
|
||||||
// FollowersURI
|
// FollowersURI
|
||||||
// TODO
|
if accountable.GetActivityStreamsFollowers() == nil || accountable.GetActivityStreamsFollowers().GetIRI() == nil {
|
||||||
|
return nil, fmt.Errorf("person with id %s had no followers uri", uri.String())
|
||||||
|
}
|
||||||
|
acct.FollowersURI = accountable.GetActivityStreamsFollowers().GetIRI().String()
|
||||||
|
|
||||||
// FeaturedURI
|
// FeaturedURI
|
||||||
// TODO
|
// very much optional
|
||||||
|
if accountable.GetTootFeatured() != nil && accountable.GetTootFeatured().GetIRI() != nil {
|
||||||
|
acct.FeaturedCollectionURI = accountable.GetTootFeatured().GetIRI().String()
|
||||||
|
}
|
||||||
|
|
||||||
// FeaturedTagsURI
|
// TODO: FeaturedTagsURI
|
||||||
// TODO
|
|
||||||
|
|
||||||
// alsoKnownAs
|
// TODO: alsoKnownAs
|
||||||
// TODO
|
|
||||||
|
|
||||||
// publicKey
|
// publicKey
|
||||||
// TODO
|
pkey, pkeyURL, err := extractPublicKeyForOwner(accountable, uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't get public key for person %s: %s", uri.String(), err)
|
||||||
|
}
|
||||||
|
acct.PublicKey = pkey
|
||||||
|
acct.PublicKeyURI = pkeyURL.String()
|
||||||
|
|
||||||
return acct, nil
|
return acct, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,11 @@ func (suite *ASToInternalTestSuite) SetupTest() {
|
||||||
testrig.StandardDBSetup(suite.db)
|
testrig.StandardDBSetup(suite.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ASToInternalTestSuite) TestASPersonToAccount() {
|
func (suite *ASToInternalTestSuite) TestASRepresentationToAccount() {
|
||||||
|
|
||||||
testPerson := suite.people["new_person_1"]
|
testPerson := suite.people["new_person_1"]
|
||||||
|
|
||||||
acct, err := suite.typeconverter.ASPersonToAccount(testPerson)
|
acct, err := suite.typeconverter.ASRepresentationToAccount(testPerson)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
fmt.Printf("%+v", acct)
|
fmt.Printf("%+v", acct)
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ type TypeConverter interface {
|
||||||
ACTIVITYSTREAMS MODEL TO INTERNAL (gts) MODEL
|
ACTIVITYSTREAMS MODEL TO INTERNAL (gts) MODEL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// ASPersonToAccount converts an activitystreams person into a gts model account
|
// ASPersonToAccount converts a remote account/person/application representation into a gts model account
|
||||||
ASPersonToAccount(person vocab.ActivityStreamsPerson) (*gtsmodel.Account, error)
|
ASRepresentationToAccount(accountable Accountable) (*gtsmodel.Account, error)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
INTERNAL (gts) MODEL TO ACTIVITYSTREAMS MODEL
|
INTERNAL (gts) MODEL TO ACTIVITYSTREAMS MODEL
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
package typeutils_test
|
package typeutils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
|
@ -34,7 +33,7 @@ type ConverterStandardTestSuite struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
accounts map[string]*gtsmodel.Account
|
accounts map[string]*gtsmodel.Account
|
||||||
people map[string]vocab.ActivityStreamsPerson
|
people map[string]typeutils.Accountable
|
||||||
|
|
||||||
typeconverter typeutils.TypeConverter
|
typeconverter typeutils.TypeConverter
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTestTokens returns a map of tokens keyed according to which account the token belongs to.
|
// NewTestTokens returns a map of tokens keyed according to which account the token belongs to.
|
||||||
|
|
@ -1050,14 +1051,14 @@ func NewTestActivities(accounts map[string]*gtsmodel.Account) map[string]Activit
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTestFediPeople returns a bunch of activity pub Person representations for testing converters and so on.
|
// NewTestFediPeople returns a bunch of activity pub Person representations for testing converters and so on.
|
||||||
func NewTestFediPeople() map[string]vocab.ActivityStreamsPerson {
|
func NewTestFediPeople() map[string]typeutils.Accountable {
|
||||||
new_person_1priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
new_person_1priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
new_person_1pub := &new_person_1priv.PublicKey
|
new_person_1pub := &new_person_1priv.PublicKey
|
||||||
|
|
||||||
return map[string]vocab.ActivityStreamsPerson{
|
return map[string]typeutils.Accountable{
|
||||||
"new_person_1": newPerson(
|
"new_person_1": newPerson(
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person"),
|
||||||
URLMustParse("https://unknown-instance.com/users/brand_new_person/following"),
|
URLMustParse("https://unknown-instance.com/users/brand_new_person/following"),
|
||||||
|
|
@ -1184,7 +1185,7 @@ func newPerson(
|
||||||
avatarURL *url.URL,
|
avatarURL *url.URL,
|
||||||
avatarContentType string,
|
avatarContentType string,
|
||||||
headerURL *url.URL,
|
headerURL *url.URL,
|
||||||
headerContentType string) vocab.ActivityStreamsPerson {
|
headerContentType string) typeutils.Accountable {
|
||||||
person := streams.NewActivityStreamsPerson()
|
person := streams.NewActivityStreamsPerson()
|
||||||
|
|
||||||
// id should be the activitypub URI of this user
|
// id should be the activitypub URI of this user
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue