Replace personIRIToUser with GetUserByIRI, do a WebFinger request to get correct username
This commit is contained in:
parent
3aba06d429
commit
bb38b9737f
|
@ -9,6 +9,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -1028,6 +1029,29 @@ func GetUserByName(ctx context.Context, name string) (*User, error) {
|
|||
return u, nil
|
||||
}
|
||||
|
||||
// GetUserByIRI returns user by given IRI.
|
||||
func GetUserByIRI(ctx context.Context, iri string) (*User, error) {
|
||||
if len(iri) == 0 {
|
||||
return nil, ErrUserNotExist{0, iri, 0}
|
||||
}
|
||||
iriSplit := strings.Split(iri, "/")
|
||||
if len(iriSplit) < 4 {
|
||||
return nil, errors.New("not a Person actor IRI")
|
||||
}
|
||||
if iriSplit[2] == setting.Domain {
|
||||
// Local user
|
||||
return GetUserByName(ctx, iriSplit[len(iriSplit)-1])
|
||||
}
|
||||
u := &User{LoginName: iri}
|
||||
has, err := db.GetEngine(ctx).Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUserNotExist{0, iri, 0}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// GetUserEmailsByNames returns a list of e-mails corresponds to names of users
|
||||
// that have their email notifications set to enabled or onmention.
|
||||
func GetUserEmailsByNames(ctx context.Context, names []string) []string {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/forgefed"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -51,12 +52,12 @@ func AuthorizeInteraction(ctx *context.Context) {
|
|||
ctx.ServerError("FederatedUserNew", err)
|
||||
return
|
||||
}
|
||||
name, err := activitypub.PersonIRIToName(object.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, object.GetLink().String())
|
||||
if err != nil {
|
||||
ctx.ServerError("PersonIRIToName", err)
|
||||
ctx.ServerError("GetUserByIRI", err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(setting.AppURL + name)
|
||||
ctx.Redirect(setting.AppURL + user.Name)
|
||||
case forgefed.RepositoryType:
|
||||
// Federated repository
|
||||
err = forgefed.OnRepository(object, func(r *forgefed.Repository) error {
|
||||
|
|
|
@ -5,6 +5,7 @@ package activitypub
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
@ -26,20 +27,46 @@ import (
|
|||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
|
||||
// Create a new federated user from a Person object
|
||||
func createPerson(ctx context.Context, person *ap.Person) error {
|
||||
name, err := activitypub.PersonIRIToName(person.GetLink())
|
||||
if err != nil {
|
||||
_, err := user_model.GetUserByIRI(ctx, person.GetLink().String())
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
// User already exists
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := user_model.IsUserExist(ctx, 0, name)
|
||||
personIRISplit := strings.Split(person.GetLink().String(), "/")
|
||||
if len(personIRISplit) < 4 {
|
||||
return errors.New("not a Person actor IRI")
|
||||
}
|
||||
|
||||
// Get instance by taking the domain of the IRI
|
||||
instance := personIRISplit[2]
|
||||
if instance == setting.Domain {
|
||||
// Local user
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send a WebFinger request to get the username
|
||||
uri, err := url.Parse("https://" + instance + "/.well-known/webfinger?resource=" + person.GetLink().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
resp, err := activitypub.Fetch(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var data activitypub.WebfingerJRD
|
||||
err = json.Unmarshal(resp, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subjectSplit := strings.Split(data.Subject, ":")
|
||||
if subjectSplit[0] != "acct" {
|
||||
return errors.New("subject is not an acct URI")
|
||||
}
|
||||
name := subjectSplit[1]
|
||||
|
||||
var email string
|
||||
if person.Location != nil {
|
||||
|
@ -55,7 +82,6 @@ func createPerson(ctx context.Context, person *ap.Person) error {
|
|||
|
||||
user := &user_model.User{
|
||||
Name: name,
|
||||
FullName: person.Name.String(), // May not exist!!
|
||||
Email: email,
|
||||
LoginType: auth.Federated,
|
||||
LoginName: person.GetLink().String(),
|
||||
|
@ -66,7 +92,11 @@ func createPerson(ctx context.Context, person *ap.Person) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if person.Name != nil {
|
||||
user.FullName = person.Name.String()
|
||||
}
|
||||
if person.Icon != nil {
|
||||
// Fetch and save user icon
|
||||
icon, err := ap.ToObject(person.Icon)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -75,12 +105,10 @@ func createPerson(ctx context.Context, person *ap.Person) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := activitypub.Fetch(iconURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = user_service.UploadAvatar(user, body)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -91,44 +119,13 @@ func createPerson(ctx context.Context, person *ap.Person) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set public key
|
||||
return user_model.SetUserSetting(user.ID, user_model.UserActivityPubPubPem, person.PublicKey.PublicKeyPem)
|
||||
}
|
||||
|
||||
func createPersonFromIRI(ctx context.Context, personIRI ap.IRI) error {
|
||||
ownerURL, err := url.Parse(personIRI.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Fetch person object
|
||||
resp, err := activitypub.Fetch(ownerURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse person object
|
||||
ap.ItemTyperFunc = forgefed.GetItemByType
|
||||
ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn
|
||||
ap.NotEmptyChecker = forgefed.NotEmpty
|
||||
object, err := ap.UnmarshalJSON(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create federated user
|
||||
person, err := ap.ToActor(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return createPerson(ctx, person)
|
||||
}
|
||||
|
||||
// Create a new federated repo from a Repository object
|
||||
func createRepository(ctx context.Context, repository *forgefed.Repository) error {
|
||||
err := createPersonFromIRI(ctx, repository.AttributedTo.GetLink())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := activitypub.PersonIRIToUser(ctx, repository.AttributedTo.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, repository.AttributedTo.GetLink().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -200,7 +197,7 @@ func createIssue(ctx context.Context, ticket *forgefed.Ticket) error {
|
|||
}
|
||||
|
||||
// Construct issue
|
||||
user, err := activitypub.PersonIRIToUser(ctx, ap.IRI(ticket.AttributedTo.GetLink().String()))
|
||||
user, err := user_model.GetUserByIRI(ctx, ticket.AttributedTo.GetLink().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -233,7 +230,7 @@ func createPullRequest(ctx context.Context, ticket *forgefed.Ticket) error {
|
|||
return err
|
||||
}
|
||||
|
||||
user, err := activitypub.PersonIRIToUser(ctx, ticket.AttributedTo.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, ticket.AttributedTo.GetLink().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -285,12 +282,7 @@ func createPullRequest(ctx context.Context, ticket *forgefed.Ticket) error {
|
|||
|
||||
// Create a comment
|
||||
func createComment(ctx context.Context, note *ap.Note) error {
|
||||
err := createPersonFromIRI(ctx, note.AttributedTo.GetLink())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := activitypub.PersonIRIToUser(ctx, note.AttributedTo.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, note.AttributedTo.GetLink().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ package activitypub
|
|||
import (
|
||||
"context"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
"code.gitea.io/gitea/services/activitypub"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
@ -20,9 +20,9 @@ func delete(ctx context.Context, delete ap.Delete) error {
|
|||
if actorIRI != objectIRI {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Object is the user getting deleted
|
||||
objectUser, err := activitypub.PersonIRIToUser(ctx, objectIRI)
|
||||
objectUser, err := user_model.GetUserByIRI(ctx, objectIRI.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,14 +18,14 @@ import (
|
|||
func follow(ctx context.Context, follow ap.Follow) error {
|
||||
// Actor is the user performing the follow
|
||||
actorIRI := follow.Actor.GetLink()
|
||||
actorUser, err := activitypub.PersonIRIToUser(ctx, actorIRI)
|
||||
actorUser, err := user_model.GetUserByIRI(ctx, actorIRI.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Object is the user being followed
|
||||
objectIRI := follow.Object.GetLink()
|
||||
objectUser, err := activitypub.PersonIRIToUser(ctx, objectIRI)
|
||||
objectUser, err := user_model.GetUserByIRI(ctx, objectIRI.String())
|
||||
// Must be a local user
|
||||
if err != nil || strings.Contains(objectUser.Name, "@") {
|
||||
return err
|
||||
|
@ -54,14 +54,14 @@ func unfollow(ctx context.Context, unfollow ap.Undo) error {
|
|||
|
||||
// Actor is the user performing the undo follow
|
||||
actorIRI := follow.Actor.GetLink()
|
||||
actorUser, err := activitypub.PersonIRIToUser(ctx, actorIRI)
|
||||
actorUser, err := user_model.GetUserByIRI(ctx, actorIRI.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Object is the user being unfollowed
|
||||
objectIRI := follow.Object.GetLink()
|
||||
objectUser, err := activitypub.PersonIRIToUser(ctx, objectIRI)
|
||||
objectUser, err := user_model.GetUserByIRI(ctx, objectIRI.String())
|
||||
// Must be a local user
|
||||
if err != nil || strings.Contains(objectUser.Name, "@") {
|
||||
return err
|
||||
|
|
|
@ -78,8 +78,6 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
|
|||
return
|
||||
}
|
||||
// 4. Create a federated user for the actor
|
||||
// TODO: This is a very bad place for creating federated users
|
||||
// We end up creating way more users than necessary!
|
||||
var person ap.Person
|
||||
err = person.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/services/activitypub"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
|
@ -16,7 +17,7 @@ import (
|
|||
|
||||
// Process a Like activity to star a repository
|
||||
func star(ctx context.Context, like ap.Like) (err error) {
|
||||
user, err := activitypub.PersonIRIToUser(ctx, like.Actor.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, like.Actor.GetLink().String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -33,7 +34,7 @@ func unstar(ctx context.Context, unlike ap.Undo) (err error) {
|
|||
if !ok {
|
||||
return errors.New("could not cast object to like")
|
||||
}
|
||||
user, err := activitypub.PersonIRIToUser(ctx, like.Actor.GetLink())
|
||||
user, err := user_model.GetUserByIRI(ctx, like.Actor.GetLink().String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -13,26 +13,11 @@ import (
|
|||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/activitypub"
|
||||
)
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4
|
||||
|
||||
type webfingerJRD struct {
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||
Links []*webfingerLink `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
type webfingerLink struct {
|
||||
Rel string `json:"rel,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Href string `json:"href,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Titles map[string]string `json:"titles,omitempty"`
|
||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||
}
|
||||
|
||||
// WebfingerQuery returns information about a resource
|
||||
// https://datatracker.ietf.org/doc/html/rfc7565
|
||||
func WebfingerQuery(ctx *context.Context) {
|
||||
|
@ -65,6 +50,8 @@ func WebfingerQuery(ctx *context.Context) {
|
|||
if u != nil && u.KeepEmailPrivate {
|
||||
err = user_model.ErrUserNotExist{}
|
||||
}
|
||||
case "https":
|
||||
u, err = user_model.GetUserByIRI(ctx, ctx.FormString("resource"))
|
||||
default:
|
||||
ctx.Error(http.StatusBadRequest)
|
||||
return
|
||||
|
@ -92,7 +79,7 @@ func WebfingerQuery(ctx *context.Context) {
|
|||
aliases = append(aliases, fmt.Sprintf("mailto:%s", u.Email))
|
||||
}
|
||||
|
||||
links := []*webfingerLink{
|
||||
links := []*activitypub.WebfingerLink{
|
||||
{
|
||||
Rel: "http://webfinger.net/rel/profile-page",
|
||||
Type: "text/html",
|
||||
|
@ -114,7 +101,7 @@ func WebfingerQuery(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.Resp.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
ctx.JSON(http.StatusOK, &webfingerJRD{
|
||||
ctx.JSON(http.StatusOK, &activitypub.WebfingerJRD{
|
||||
Subject: fmt.Sprintf("acct:%s@%s", url.QueryEscape(u.Name), appURL.Host),
|
||||
Aliases: aliases,
|
||||
Links: links,
|
||||
|
|
|
@ -9,40 +9,11 @@ import (
|
|||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
// Returns the username corresponding to a Person actor IRI
|
||||
func PersonIRIToName(personIRI ap.IRI) (string, error) {
|
||||
personIRISplit := strings.Split(personIRI.String(), "/")
|
||||
if len(personIRISplit) < 4 {
|
||||
return "", errors.New("not a Person actor IRI")
|
||||
}
|
||||
|
||||
instance := personIRISplit[2]
|
||||
name := personIRISplit[len(personIRISplit)-1]
|
||||
if instance == setting.Domain {
|
||||
// Local user
|
||||
return name, nil
|
||||
}
|
||||
// Remote user
|
||||
// Get name in username@instance.com format
|
||||
return name + "@" + instance, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user_model.GetUserByName(ctx, name)
|
||||
}
|
||||
|
||||
// Returns the owner and name corresponding to a Repository actor IRI
|
||||
func RepositoryIRIToName(repoIRI ap.IRI) (string, string, error) {
|
||||
repoIRISplit := strings.Split(repoIRI.String(), "/")
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package activitypub;
|
||||
|
||||
type WebfingerJRD struct {
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||
Links []*WebfingerLink `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
type WebfingerLink struct {
|
||||
Rel string `json:"rel,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Href string `json:"href,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Titles map[string]string `json:"titles,omitempty"`
|
||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||
}
|
Reference in New Issue