Implement sending follow activities

I moved around a lot of files to fix import cycles
This commit is contained in:
Anthony Wang 2022-10-21 21:06:59 +00:00
parent e78dd699de
commit f133e9ca11
Signed by: a
GPG key ID: 42A5B952E6DD8D38
13 changed files with 81 additions and 38 deletions

View file

@ -17,7 +17,7 @@ import (
// Create a comment
func Comment(ctx context.Context, note *ap.Note) error {
actorUser, err := personIRIToUser(ctx, note.AttributedTo.GetLink())
actorUser, err := PersonIRIToUser(ctx, note.AttributedTo.GetLink())
if err != nil {
return err
}

View file

@ -17,14 +17,14 @@ import (
func Follow(ctx context.Context, follow ap.Follow) error {
// Actor is the user performing the follow
actorIRI := follow.Actor.GetLink()
actorUser, err := personIRIToUser(ctx, actorIRI)
actorUser, err := PersonIRIToUser(ctx, actorIRI)
if err != nil {
return err
}
// Object is the user being followed
objectIRI := follow.Object.GetLink()
objectUser, err := personIRIToUser(ctx, objectIRI)
objectUser, err := PersonIRIToUser(ctx, objectIRI)
// Must be a local user
if err != nil || strings.Contains(objectUser.Name, "@") {
return err
@ -48,14 +48,14 @@ func Unfollow(ctx context.Context, unfollow ap.Undo) error {
follow := unfollow.Object.(*ap.Follow)
// Actor is the user performing the undo follow
actorIRI := follow.Actor.GetLink()
actorUser, err := personIRIToUser(ctx, actorIRI)
actorUser, err := PersonIRIToUser(ctx, actorIRI)
if err != nil {
return err
}
// Object is the user being unfollowed
objectIRI := follow.Object.GetLink()
objectUser, err := personIRIToUser(ctx, objectIRI)
objectUser, err := PersonIRIToUser(ctx, objectIRI)
// Must be a local user
if err != nil || strings.Contains(objectUser.Name, "@") {
return err

View file

@ -52,7 +52,7 @@ func ReceiveFork(ctx context.Context, create ap.Create) error {
repository := create.Object.(*forgefed.Repository)
actor, err := personIRIToUser(ctx, create.Actor.GetLink())
actor, err := PersonIRIToUser(ctx, create.Actor.GetLink())
if err != nil {
return err
}
@ -62,7 +62,7 @@ func ReceiveFork(ctx context.Context, create ap.Create) error {
// Create the fork
repoIRI := repository.GetLink()
username, reponame, err := repositoryIRIToName(repoIRI)
username, reponame, err := RepositoryIRIToName(repoIRI)
if err != nil {
return err
}

View file

@ -17,7 +17,7 @@ import (
)
// Returns the username corresponding to a Person actor IRI
func personIRIToName(personIRI ap.IRI) (string, error) {
func PersonIRIToName(personIRI ap.IRI) (string, error) {
personIRISplit := strings.Split(personIRI.String(), "/")
if len(personIRISplit) < 3 {
return "", errors.New("Not a Person actor IRI")
@ -35,8 +35,8 @@ func personIRIToName(personIRI ap.IRI) (string, error) {
}
// Returns the user corresponding to a Person actor IRI
func personIRIToUser(ctx context.Context, personIRI ap.IRI) (*user_model.User, error) {
name, err := personIRIToName(personIRI)
func PersonIRIToUser(ctx context.Context, personIRI ap.IRI) (*user_model.User, error) {
name, err := PersonIRIToName(personIRI)
if err != nil {
return nil, err
}
@ -50,7 +50,7 @@ func personIRIToUser(ctx context.Context, personIRI ap.IRI) (*user_model.User, e
}
// Returns the owner and name corresponding to a Repository actor IRI
func repositoryIRIToName(repoIRI ap.IRI) (string, string, error) {
func RepositoryIRIToName(repoIRI ap.IRI) (string, string, error) {
repoIRISplit := strings.Split(repoIRI.String(), "/")
if len(repoIRISplit) < 5 {
return "", "", errors.New("Not a Repository actor IRI")
@ -68,8 +68,8 @@ func repositoryIRIToName(repoIRI ap.IRI) (string, string, error) {
}
// Returns the repository corresponding to a Repository actor IRI
func repositoryIRIToRepository(ctx context.Context, repoIRI ap.IRI) (*repo_model.Repository, error) {
username, reponame, err := repositoryIRIToName(repoIRI)
func RepositoryIRIToRepository(ctx context.Context, repoIRI ap.IRI) (*repo_model.Repository, error) {
username, reponame, err := RepositoryIRIToName(repoIRI)
if err != nil {
return nil, err
}

View file

@ -18,7 +18,7 @@ import (
func PullRequest(ctx context.Context, ticket *forgefed.Ticket) error {
// TODO: Clean this up
actorUser, err := personIRIToUser(ctx, ticket.AttributedTo.GetLink())
actorUser, err := PersonIRIToUser(ctx, ticket.AttributedTo.GetLink())
if err != nil {
log.Warn("Couldn't find ticket actor user", err)
}

View file

@ -16,7 +16,7 @@ import (
// Create a new federated repo from a Repository object
func FederatedRepoNew(ctx context.Context, repository *forgefed.Repository) error {
ownerIRI := repository.AttributedTo.GetLink()
user, err := personIRIToUser(ctx, ownerIRI)
user, err := PersonIRIToUser(ctx, ownerIRI)
if err != nil {
return err
}
@ -25,7 +25,6 @@ func FederatedRepoNew(ctx context.Context, repository *forgefed.Repository) erro
if err == nil {
return nil
}
repo, err := repo_service.CreateRepository(user, user, repo_module.CreateRepoOptions{
Name: repository.Name.String(),
@ -36,7 +35,7 @@ func FederatedRepoNew(ctx context.Context, repository *forgefed.Repository) erro
if repository.ForkedFrom != nil {
repo.IsFork = true
forkedFrom, err := repositoryIRIToRepository(ctx, repository.ForkedFrom.GetLink())
forkedFrom, err := RepositoryIRIToRepository(ctx, repository.ForkedFrom.GetLink())
if err != nil {
return err
}

View file

@ -15,11 +15,11 @@ import (
// Process a Like activity to star a repository
func ReceiveStar(ctx context.Context, like ap.Like) (err error) {
user, err := personIRIToUser(ctx, like.Actor.GetLink())
user, err := PersonIRIToUser(ctx, like.Actor.GetLink())
if err != nil {
return
}
repo, err := repositoryIRIToRepository(ctx, like.Object.GetLink())
repo, err := RepositoryIRIToRepository(ctx, like.Object.GetLink())
if err != nil || strings.Contains(repo.Name, "@") || repo.IsPrivate {
return
}

View file

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/activitypub"
gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user"
ap "github.com/go-ap/activitypub"
"github.com/go-fed/httpsig"
@ -84,7 +85,7 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
return
}
err = activitypub.FederatedUserNew(ctx, &person)
err = user_service.FederatedUserNew(ctx, &person)
return authenticated, err
}

View file

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
user_service "code.gitea.io/gitea/services/user"
)
func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
@ -219,7 +220,7 @@ func Follow(ctx *context.APIContext) {
// "204":
// "$ref": "#/responses/empty"
if err := user_model.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
if err := user_service.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
ctx.Error(http.StatusInternalServerError, "FollowUser", err)
return
}
@ -241,7 +242,7 @@ func Unfollow(ctx *context.APIContext) {
// "204":
// "$ref": "#/responses/empty"
if err := user_model.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
if err := user_service.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
ctx.Error(http.StatusInternalServerError, "UnfollowUser", err)
return
}

View file

@ -2,14 +2,16 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package activitypub
package web
import (
"net/http"
"net/url"
"code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/forgefed"
user_service "code.gitea.io/gitea/services/user"
ap "github.com/go-ap/activitypub"
)
@ -20,7 +22,7 @@ func AuthorizeInteraction(ctx *context.Context) {
ctx.ServerError("Could not parse URI", err)
return
}
resp, err := Fetch(uri)
resp, err := activitypub.Fetch(uri)
if err != nil {
ctx.ServerError("Fetch", err)
return
@ -40,12 +42,12 @@ func AuthorizeInteraction(ctx *context.Context) {
ctx.ServerError("UnmarshalJSON", err)
return
}
err = FederatedUserNew(ctx, object.(*ap.Person))
err = user_service.FederatedUserNew(ctx, object.(*ap.Person))
if err != nil {
ctx.ServerError("FederatedUserNew", err)
return
}
name, err := personIRIToName(object.GetLink())
name, err := activitypub.PersonIRIToName(object.GetLink())
if err != nil {
ctx.ServerError("personIRIToName", err)
return
@ -53,13 +55,13 @@ func AuthorizeInteraction(ctx *context.Context) {
ctx.Redirect(name)
case forgefed.RepositoryType:
err = forgefed.OnRepository(object, func(r *forgefed.Repository) error {
return FederatedRepoNew(ctx, r)
return activitypub.FederatedRepoNew(ctx, r)
})
if err != nil {
ctx.ServerError("FederatedRepoNew", err)
return
}
username, reponame, err := repositoryIRIToName(object.GetLink())
username, reponame, err := activitypub.RepositoryIRIToName(object.GetLink())
if err != nil {
ctx.ServerError("repositoryIRIToName", err)
return
@ -67,7 +69,7 @@ func AuthorizeInteraction(ctx *context.Context) {
ctx.Redirect(username + "/" + reponame)
case forgefed.TicketType:
err = forgefed.OnTicket(object, func(t *forgefed.Ticket) error {
return ReceiveIssue(ctx, t)
return activitypub.ReceiveIssue(ctx, t)
})
if err != nil {
ctx.ServerError("ReceiveIssue", err)

View file

@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/feed"
"code.gitea.io/gitea/routers/web/org"
user_service "code.gitea.io/gitea/services/user"
)
// Profile render user's profile page
@ -302,9 +303,9 @@ func Action(ctx *context.Context) {
var err error
switch ctx.FormString("action") {
case "follow":
err = user_model.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
err = user_service.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
case "unfollow":
err = user_model.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
err = user_service.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
}
if err != nil {

View file

@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
@ -1317,7 +1316,7 @@ func RegisterRoutes(m *web.Route) {
}, reqSignIn)
if setting.Federation.Enabled {
m.Get("/authorize_interaction", activitypub.AuthorizeInteraction)
m.Get("/authorize_interaction", AuthorizeInteraction)
}
if setting.API.EnableSwagger {

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package activitypub
package user
import (
"context"
@ -11,15 +11,55 @@ import (
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/activitypub"
"code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user"
ap "github.com/go-ap/activitypub"
)
// FollowUser marks someone be another's follower.
func FollowUser(userID, followID int64) (err error) {
if userID == followID || user_model.IsFollowing(userID, followID) {
return nil
}
followUser, err := user_model.GetUserByID(followID)
if err != nil {
return err
}
if followUser.LoginType == auth.Federated {
// Following remote user
actorUser, err := user_model.GetUserByID(userID)
if err != nil {
return err
}
object := ap.PersonNew(ap.IRI(followUser.LoginName))
follow := ap.FollowNew("", object)
follow.Type = ap.FollowType
follow.Actor = ap.PersonNew(ap.IRI(setting.AppURL+"api/v1/activitypub/user/"+actorUser.Name))
follow.To = ap.ItemCollection{ap.Item(ap.IRI(followUser.LoginName+"/inbox"))}
err = activitypub.Send(actorUser, follow)
if err != nil {
return err
}
}
return user_model.FollowUser(userID, followID)
}
// UnfollowUser unmarks someone as another's follower.
func UnfollowUser(userID, followID int64) (err error) {
if userID == followID || !user_model.IsFollowing(userID, followID) {
return nil
}
return user_model.UnfollowUser(userID, followID)
}
// Create a new federated user from a Person object
func FederatedUserNew(ctx context.Context, person *ap.Person) error {
name, err := personIRIToName(person.GetLink())
name, err := activitypub.PersonIRIToName(person.GetLink())
if err != nil {
return err
}
@ -63,12 +103,12 @@ func FederatedUserNew(ctx context.Context, person *ap.Person) error {
return err
}
body, err := Fetch(iconURL)
body, err := activitypub.Fetch(iconURL)
if err != nil {
return err
}
err = user_service.UploadAvatar(user, body)
err = UploadAvatar(user, body)
if err != nil {
return err
}