2021-05-17 18:06:58 +01:00
/ *
GoToSocial
Copyright ( C ) 2021 GoToSocial Authors admin @ gotosocial . org
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/>.
* /
2021-05-30 12:12:00 +01:00
package processing
2021-05-08 13:25:55 +01:00
import (
2021-05-15 10:58:11 +01:00
"context"
2021-05-08 13:25:55 +01:00
"fmt"
"net/http"
2021-05-21 14:48:26 +01:00
"net/url"
2021-05-08 13:25:55 +01:00
"github.com/go-fed/activity/streams"
2021-05-09 19:34:27 +01:00
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
2021-05-08 13:25:55 +01:00
"github.com/superseriousbusiness/gotosocial/internal/db"
2021-06-13 17:42:28 +01:00
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
2021-05-08 13:25:55 +01:00
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
2021-06-13 17:42:28 +01:00
"github.com/superseriousbusiness/gotosocial/internal/id"
2021-05-15 10:58:11 +01:00
"github.com/superseriousbusiness/gotosocial/internal/util"
2021-05-08 13:25:55 +01:00
)
// authenticateAndDereferenceFediRequest authenticates the HTTP signature of an incoming federation request, using the given
// username to perform the validation. It will *also* dereference the originator of the request and return it as a gtsmodel account
// for further processing. NOTE that this function will have the side effect of putting the dereferenced account into the database,
// and passing it into the processor through a channel for further asynchronous processing.
func ( p * processor ) authenticateAndDereferenceFediRequest ( username string , r * http . Request ) ( * gtsmodel . Account , error ) {
// first authenticate
requestingAccountURI , err := p . federator . AuthenticateFederatedRequest ( username , r )
if err != nil {
return nil , fmt . Errorf ( "couldn't authenticate request for username %s: %s" , username , err )
}
// OK now we can do the dereferencing part
// we might already have an entry for this account so check that first
requestingAccount := & gtsmodel . Account { }
2021-05-21 14:48:26 +01:00
err = p . db . GetWhere ( [ ] db . Where { { Key : "uri" , Value : requestingAccountURI . String ( ) } } , requestingAccount )
2021-05-08 13:25:55 +01:00
if err == nil {
// we do have it yay, return it
return requestingAccount , nil
}
if _ , ok := err . ( db . ErrNoEntries ) ; ! ok {
// something has actually gone wrong so bail
return nil , fmt . Errorf ( "database error getting account with uri %s: %s" , requestingAccountURI . String ( ) , err )
}
// we just don't have an entry for this account yet
// what we do now should depend on our chosen federation method
// for now though, we'll just dereference it
// TODO: slow-fed
requestingPerson , err := p . federator . DereferenceRemoteAccount ( username , requestingAccountURI )
if err != nil {
return nil , fmt . Errorf ( "couldn't dereference %s: %s" , requestingAccountURI . String ( ) , err )
}
// convert it to our internal account representation
2021-05-23 17:07:04 +01:00
requestingAccount , err = p . tc . ASRepresentationToAccount ( requestingPerson , false )
2021-05-08 13:25:55 +01:00
if err != nil {
return nil , fmt . Errorf ( "couldn't convert dereferenced uri %s to gtsmodel account: %s" , requestingAccountURI . String ( ) , err )
}
2021-06-13 17:42:28 +01:00
requestingAccountID , err := id . NewRandomULID ( )
if err != nil {
return nil , err
}
requestingAccount . ID = requestingAccountID
2021-05-08 13:25:55 +01:00
if err := p . db . Put ( requestingAccount ) ; err != nil {
return nil , fmt . Errorf ( "database error inserting account with uri %s: %s" , requestingAccountURI . String ( ) , err )
}
// put it in our channel to queue it for async processing
2021-06-13 17:42:28 +01:00
p . fromFederator <- gtsmodel . FromFederator {
2021-05-08 13:25:55 +01:00
APObjectType : gtsmodel . ActivityStreamsProfile ,
APActivityType : gtsmodel . ActivityStreamsCreate ,
2021-05-17 18:06:58 +01:00
GTSModel : requestingAccount ,
2021-05-08 13:25:55 +01:00
}
return requestingAccount , nil
}
2021-06-13 17:42:28 +01:00
func ( p * processor ) GetFediUser ( requestedUsername string , request * http . Request ) ( interface { } , gtserror . WithCode ) {
2021-05-08 13:25:55 +01:00
// get the account the request is referring to
requestedAccount := & gtsmodel . Account { }
if err := p . db . GetLocalAccountByUsername ( requestedUsername , requestedAccount ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting account with username %s: %s" , requestedUsername , err ) )
2021-05-08 13:25:55 +01:00
}
// authenticate the request
requestingAccount , err := p . authenticateAndDereferenceFediRequest ( requestedUsername , request )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( err )
2021-05-08 13:25:55 +01:00
}
blocked , err := p . db . Blocked ( requestedAccount . ID , requestingAccount . ID )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-08 13:25:55 +01:00
}
if blocked {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( fmt . Errorf ( "block exists between accounts %s and %s" , requestedAccount . ID , requestingAccount . ID ) )
2021-05-08 13:25:55 +01:00
}
requestedPerson , err := p . tc . AccountToAS ( requestedAccount )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-08 13:25:55 +01:00
}
data , err := streams . Serialize ( requestedPerson )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-08 13:25:55 +01:00
}
return data , nil
}
2021-05-09 19:34:27 +01:00
2021-06-13 17:42:28 +01:00
func ( p * processor ) GetFediFollowers ( requestedUsername string , request * http . Request ) ( interface { } , gtserror . WithCode ) {
2021-05-21 14:48:26 +01:00
// get the account the request is referring to
requestedAccount := & gtsmodel . Account { }
if err := p . db . GetLocalAccountByUsername ( requestedUsername , requestedAccount ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting account with username %s: %s" , requestedUsername , err ) )
2021-05-21 14:48:26 +01:00
}
// authenticate the request
requestingAccount , err := p . authenticateAndDereferenceFediRequest ( requestedUsername , request )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( err )
2021-05-21 14:48:26 +01:00
}
blocked , err := p . db . Blocked ( requestedAccount . ID , requestingAccount . ID )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-21 14:48:26 +01:00
}
if blocked {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( fmt . Errorf ( "block exists between accounts %s and %s" , requestedAccount . ID , requestingAccount . ID ) )
2021-05-21 14:48:26 +01:00
}
requestedAccountURI , err := url . Parse ( requestedAccount . URI )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( fmt . Errorf ( "error parsing url %s: %s" , requestedAccount . URI , err ) )
2021-05-21 14:48:26 +01:00
}
requestedFollowers , err := p . federator . FederatingDB ( ) . Followers ( context . Background ( ) , requestedAccountURI )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( fmt . Errorf ( "error fetching followers for uri %s: %s" , requestedAccountURI . String ( ) , err ) )
2021-05-21 14:48:26 +01:00
}
data , err := streams . Serialize ( requestedFollowers )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-21 14:48:26 +01:00
}
return data , nil
}
2021-06-13 17:42:28 +01:00
func ( p * processor ) GetFediFollowing ( requestedUsername string , request * http . Request ) ( interface { } , gtserror . WithCode ) {
2021-05-23 17:07:04 +01:00
// get the account the request is referring to
requestedAccount := & gtsmodel . Account { }
if err := p . db . GetLocalAccountByUsername ( requestedUsername , requestedAccount ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting account with username %s: %s" , requestedUsername , err ) )
2021-05-23 17:07:04 +01:00
}
// authenticate the request
requestingAccount , err := p . authenticateAndDereferenceFediRequest ( requestedUsername , request )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( err )
2021-05-23 17:07:04 +01:00
}
blocked , err := p . db . Blocked ( requestedAccount . ID , requestingAccount . ID )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-23 17:07:04 +01:00
}
if blocked {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( fmt . Errorf ( "block exists between accounts %s and %s" , requestedAccount . ID , requestingAccount . ID ) )
2021-05-23 17:07:04 +01:00
}
requestedAccountURI , err := url . Parse ( requestedAccount . URI )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( fmt . Errorf ( "error parsing url %s: %s" , requestedAccount . URI , err ) )
2021-05-23 17:07:04 +01:00
}
requestedFollowing , err := p . federator . FederatingDB ( ) . Following ( context . Background ( ) , requestedAccountURI )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( fmt . Errorf ( "error fetching following for uri %s: %s" , requestedAccountURI . String ( ) , err ) )
2021-05-23 17:07:04 +01:00
}
data , err := streams . Serialize ( requestedFollowing )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-23 17:07:04 +01:00
}
return data , nil
}
2021-06-13 17:42:28 +01:00
func ( p * processor ) GetFediStatus ( requestedUsername string , requestedStatusID string , request * http . Request ) ( interface { } , gtserror . WithCode ) {
2021-05-21 22:04:59 +01:00
// get the account the request is referring to
requestedAccount := & gtsmodel . Account { }
if err := p . db . GetLocalAccountByUsername ( requestedUsername , requestedAccount ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting account with username %s: %s" , requestedUsername , err ) )
2021-05-21 22:04:59 +01:00
}
// authenticate the request
requestingAccount , err := p . authenticateAndDereferenceFediRequest ( requestedUsername , request )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( err )
2021-05-21 22:04:59 +01:00
}
2021-06-17 17:02:33 +01:00
// authorize the request:
// 1. check if a block exists between the requester and the requestee
2021-05-21 22:04:59 +01:00
blocked , err := p . db . Blocked ( requestedAccount . ID , requestingAccount . ID )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-21 22:04:59 +01:00
}
if blocked {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotAuthorized ( fmt . Errorf ( "block exists between accounts %s and %s" , requestedAccount . ID , requestingAccount . ID ) )
2021-05-21 22:04:59 +01:00
}
2021-06-17 17:02:33 +01:00
// get the status out of the database here
2021-05-21 22:04:59 +01:00
s := & gtsmodel . Status { }
if err := p . db . GetWhere ( [ ] db . Where {
{ Key : "id" , Value : requestedStatusID } ,
{ Key : "account_id" , Value : requestedAccount . ID } ,
} , s ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting status with id %s and account id %s: %s" , requestedStatusID , requestedAccount . ID , err ) )
2021-05-21 22:04:59 +01:00
}
2021-06-17 17:02:33 +01:00
visible , err := p . filter . StatusVisible ( s , requestingAccount )
if err != nil {
return nil , gtserror . NewErrorInternalError ( err )
}
if ! visible {
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "status with id %s not visible to user with id %s" , s . ID , requestingAccount . ID ) )
}
// requester is authorized to view the status, so convert it to AP representation and serialize it
2021-05-21 22:04:59 +01:00
asStatus , err := p . tc . StatusToAS ( s )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-21 22:04:59 +01:00
}
data , err := streams . Serialize ( asStatus )
if err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorInternalError ( err )
2021-05-21 22:04:59 +01:00
}
return data , nil
2021-05-21 14:48:26 +01:00
}
2021-06-24 13:26:08 +01:00
func ( p * processor ) GetWebfingerAccount ( requestedUsername string , request * http . Request ) ( * apimodel . WellKnownResponse , gtserror . WithCode ) {
2021-05-09 19:34:27 +01:00
// get the account the request is referring to
requestedAccount := & gtsmodel . Account { }
if err := p . db . GetLocalAccountByUsername ( requestedUsername , requestedAccount ) ; err != nil {
2021-06-13 17:42:28 +01:00
return nil , gtserror . NewErrorNotFound ( fmt . Errorf ( "database error getting account with username %s: %s" , requestedUsername , err ) )
2021-05-09 19:34:27 +01:00
}
// return the webfinger representation
2021-06-24 13:26:08 +01:00
return & apimodel . WellKnownResponse {
2021-05-09 19:34:27 +01:00
Subject : fmt . Sprintf ( "acct:%s@%s" , requestedAccount . Username , p . config . Host ) ,
Aliases : [ ] string {
requestedAccount . URI ,
requestedAccount . URL ,
} ,
2021-06-24 13:26:08 +01:00
Links : [ ] apimodel . Link {
2021-05-09 19:34:27 +01:00
{
Rel : "http://webfinger.net/rel/profile-page" ,
Type : "text/html" ,
Href : requestedAccount . URL ,
} ,
{
Rel : "self" ,
Type : "application/activity+json" ,
Href : requestedAccount . URI ,
} ,
} ,
} , nil
}
2021-05-15 10:58:11 +01:00
2021-06-24 13:26:08 +01:00
func ( p * processor ) GetNodeInfoRel ( request * http . Request ) ( * apimodel . WellKnownResponse , gtserror . WithCode ) {
return & apimodel . WellKnownResponse {
Links : [ ] apimodel . Link {
{
Rel : "http://nodeinfo.diaspora.software/ns/schema/2.0" ,
Href : fmt . Sprintf ( "%s://%s/nodeinfo/2.0" , p . config . Protocol , p . config . Host ) ,
} ,
} ,
} , nil
}
func ( p * processor ) GetNodeInfo ( request * http . Request ) ( * apimodel . Nodeinfo , gtserror . WithCode ) {
return & apimodel . Nodeinfo {
Version : "2.0" ,
Software : apimodel . NodeInfoSoftware {
Name : "gotosocial" ,
Version : p . config . SoftwareVersion ,
} ,
Protocols : [ ] string { "activitypub" } ,
Services : apimodel . NodeInfoServices {
Inbound : [ ] string { } ,
Outbound : [ ] string { } ,
} ,
OpenRegistrations : p . config . AccountsConfig . OpenRegistration ,
Usage : apimodel . NodeInfoUsage {
Users : apimodel . NodeInfoUsers { } ,
} ,
Metadata : make ( map [ string ] interface { } ) ,
} , nil
}
2021-05-15 10:58:11 +01:00
func ( p * processor ) InboxPost ( ctx context . Context , w http . ResponseWriter , r * http . Request ) ( bool , error ) {
contextWithChannel := context . WithValue ( ctx , util . APFromFederatorChanKey , p . fromFederator )
posted , err := p . federator . FederatingActor ( ) . PostInbox ( contextWithChannel , w , r )
return posted , err
}