From 66bc7a9a0c4650727b2d8fc36619c028511f21de Mon Sep 17 00:00:00 2001
From: Michael Jerger <michael.jerger@meissa-gmbh.de>
Date: Thu, 8 Feb 2024 11:09:55 +0100
Subject: [PATCH] Search federatedUser instead of loginName

---
 models/forgefed/federationhost_repository.go |  1 -
 models/user/user.go                          | 11 ++++
 models/user/user_repository.go               | 26 +++++++++
 routers/api/v1/activitypub/repository.go     | 60 +++-----------------
 4 files changed, 46 insertions(+), 52 deletions(-)

diff --git a/models/forgefed/federationhost_repository.go b/models/forgefed/federationhost_repository.go
index 37987d5720..4adb7a5c13 100644
--- a/models/forgefed/federationhost_repository.go
+++ b/models/forgefed/federationhost_repository.go
@@ -32,7 +32,6 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) {
 
 func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) {
 	host := new(FederationHost)
-	// TODO: use parameter with toLower
 	has, err := db.GetEngine(ctx).Where("host_fqdn=?", strings.ToLower(fqdn)).Get(host)
 	if err != nil {
 		return nil, err
diff --git a/models/user/user.go b/models/user/user.go
index 71f823dc31..5490bd4cf1 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -811,6 +811,17 @@ func ValidateUser(u *User, cols ...string) error {
 	return nil
 }
 
+func (user User) Validate() []string {
+	var result []string
+	if err := ValidateUser(&user); err != nil {
+		result = append(result, err.Error())
+	}
+	if err := ValidateEmail(user.Email); err != nil {
+		result = append(result, err.Error())
+	}
+	return result
+}
+
 // UpdateUserCols update user according special columns
 func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
 	if err := ValidateUser(u, cols...); err != nil {
diff --git a/models/user/user_repository.go b/models/user/user_repository.go
index e6d625aae5..f4d34318fc 100644
--- a/models/user/user_repository.go
+++ b/models/user/user_repository.go
@@ -22,3 +22,29 @@ func CreateFederationUser(ctx context.Context, user *FederatedUser) error {
 	_, err := db.GetEngine(ctx).Insert(user)
 	return err
 }
+
+func FindFederatedUser(ctx context.Context, externalID string,
+	federationHostID int64) (*User, *FederatedUser, error) {
+	federatedUser := new(FederatedUser)
+	user := new(User)
+	has, err := db.GetEngine(ctx).Where("external_id=? and federation_host_id=?", externalID, federationHostID).Get(federatedUser)
+	if err != nil {
+		return nil, nil, err
+	} else if !has {
+		return nil, nil, nil
+	}
+	has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user)
+	if err != nil {
+		return nil, nil, err
+	} else if !has {
+		return nil, nil, fmt.Errorf("User %v for federated user is missing.", federatedUser.UserID)
+	}
+
+	if res, err := validation.IsValid(*user); !res {
+		return nil, nil, fmt.Errorf("FederatedUser is not valid: %v", err)
+	}
+	if res, err := validation.IsValid(*federatedUser); !res {
+		return nil, nil, fmt.Errorf("FederatedUser is not valid: %v", err)
+	}
+	return user, federatedUser, nil
+}
diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go
index e7052625f5..ddfcab7151 100644
--- a/routers/api/v1/activitypub/repository.go
+++ b/routers/api/v1/activitypub/repository.go
@@ -8,7 +8,6 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/forgefed"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -16,7 +15,6 @@ import (
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/modules/web"
 
@@ -74,8 +72,6 @@ func RepositoryInbox(ctx *context.APIContext) {
 	//   "204":
 	//     "$ref": "#/responses/empty"
 
-	var user *user_model.User
-
 	repository := ctx.Repo.Repository
 	log.Info("RepositoryInbox: repo: %v", repository)
 
@@ -137,37 +133,21 @@ func RepositoryInbox(ctx *context.APIContext) {
 	log.Info("RepositoryInbox: remoteStargazer: %v", actorAsLoginID)
 
 	// Check if user already exists
-	// TODO: search for federation user instead
-	// users, _, err := SearchFederatedUser(actorID.ID, federationHost.ID)
-	users, err := SearchUsersByLoginName(actorAsLoginID)
+	user, _, err := user_model.FindFederatedUser(ctx, actorID.ID, federationHost.ID)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "RepositoryInbox: Searching for user failed", err)
 		return
 	}
-	log.Info("RepositoryInbox: local found users: %v", len(users))
-
-	switch len(users) {
-	case 0:
-		{
-			user, err = createUserFromAP(ctx, actorID, federationHost.ID)
-			if err != nil {
-				ctx.Error(http.StatusInternalServerError,
-					"RepositoryInbox: Creating federated user failed", err)
-				return
-			}
-			log.Info("RepositoryInbox: created user from ap: %v", user)
-		}
-	case 1:
-		{
-			user = users[0]
-			log.Info("RepositoryInbox: found user: %v", user)
-		}
-	default:
-		{
-			ctx.Error(http.StatusInternalServerError, "RepositoryInbox",
-				fmt.Errorf(" more than one matches for federated users"))
+	if user != nil {
+		log.Info("RepositoryInbox: found user: %v", user)
+	} else {
+		user, err = createUserFromAP(ctx, actorID, federationHost.ID)
+		if err != nil {
+			ctx.Error(http.StatusInternalServerError,
+				"RepositoryInbox: Creating federated user failed", err)
 			return
 		}
+		log.Info("RepositoryInbox: created user from ap: %v", user)
 	}
 
 	// execute the activity if the repo was not stared already
@@ -189,28 +169,6 @@ func RepositoryInbox(ctx *context.APIContext) {
 	ctx.Status(http.StatusNoContent)
 }
 
-// TODO: Move this to model.user.search ? or to model.user.externalLoginUser ?
-func SearchUsersByLoginName(loginName string) ([]*user_model.User, error) {
-	actionsUser := user_model.NewActionsUser()
-	actionsUser.IsAdmin = true
-
-	options := &user_model.SearchUserOptions{
-		LoginName:       loginName,
-		Actor:           actionsUser,
-		Type:            user_model.UserTypeRemoteUser,
-		OrderBy:         db.SearchOrderByAlphabetically,
-		ListOptions:     db.ListOptions{PageSize: 1},
-		IsActive:        util.OptionalBoolFalse,
-		IncludeReserved: true,
-	}
-	users, _, err := user_model.SearchUsers(db.DefaultContext, options)
-	if err != nil {
-		return []*user_model.User{}, fmt.Errorf("search failed: %v", err)
-	}
-
-	return users, nil
-}
-
 func createFederationHost(ctx *context.APIContext, actorID forgefed.ActorID) (forgefed.FederationHost, error) {
 	actionsUser := user_model.NewActionsUser()
 	client, err := api.NewClient(ctx, actionsUser, "no idea where to get key material.")