From b2c3eb1644e39ddb8434c42744a29c8464558adb Mon Sep 17 00:00:00 2001
From: Michael Jerger <michael.jerger@meissa-gmbh.de>
Date: Thu, 16 May 2024 18:25:16 +0200
Subject: [PATCH] add migration & enhance int-test

---
 models/forgejo_migrations/migrate.go            |  4 ++++
 models/forgejo_migrations/v16.go                | 17 +++++++++++++++++
 models/forgejo_migrations/v17.go                | 14 ++++++++++++++
 services/federation/federation_service.go       |  2 +-
 .../api_activitypub_repository_test.go          |  8 +++++---
 5 files changed, 41 insertions(+), 4 deletions(-)
 create mode 100644 models/forgejo_migrations/v16.go
 create mode 100644 models/forgejo_migrations/v17.go

diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go
index fc5a460163..85229994b4 100644
--- a/models/forgejo_migrations/migrate.go
+++ b/models/forgejo_migrations/migrate.go
@@ -68,6 +68,10 @@ var migrations = []*Migration{
 	NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge),
 	// v15 -> v16
 	NewMigration("Create the `federation_host` table", CreateFederationHostTable),
+	// v16 -> v17
+	NewMigration("Create the `federated_user` table", CreateFederatedUserTable),
+	// v17 -> v18
+	NewMigration("Add `normalized_federated_uri` column to `user` table", AddNormalizedFederatedURIToUser),
 }
 
 // GetCurrentDBVersion returns the current Forgejo database version.
diff --git a/models/forgejo_migrations/v16.go b/models/forgejo_migrations/v16.go
new file mode 100644
index 0000000000..f80bfc5268
--- /dev/null
+++ b/models/forgejo_migrations/v16.go
@@ -0,0 +1,17 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package forgejo_migrations //nolint:revive
+
+import "xorm.io/xorm"
+
+type FederatedUser struct {
+	ID               int64  `xorm:"pk autoincr"`
+	UserID           int64  `xorm:"NOT NULL"`
+	ExternalID       string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
+	FederationHostID int64  `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
+}
+
+func CreateFederatedUserTable(x *xorm.Engine) error {
+	return x.Sync(new(FederatedUser))
+}
diff --git a/models/forgejo_migrations/v17.go b/models/forgejo_migrations/v17.go
new file mode 100644
index 0000000000..d6e2983d00
--- /dev/null
+++ b/models/forgejo_migrations/v17.go
@@ -0,0 +1,14 @@
+// Copyright 2024 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package forgejo_migrations //nolint:revive
+
+import "xorm.io/xorm"
+
+func AddNormalizedFederatedURIToUser(x *xorm.Engine) error {
+	type User struct {
+		ID                     int64 `xorm:"pk autoincr"`
+		NormalizedFederatedURI string
+	}
+	return x.Sync(&User{})
+}
diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go
index b8215c4728..0e7efa133a 100644
--- a/services/federation/federation_service.go
+++ b/services/federation/federation_service.go
@@ -173,7 +173,7 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI
 		return nil, nil, err
 	}
 	newUser := user.User{
-		LowerName:                    strings.ToLower(person.PreferredUsername.String()),
+		LowerName:                    strings.ToLower(name),
 		Name:                         name,
 		FullName:                     fullName,
 		Email:                        email,
diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go
index 67b18dac58..acb77378a1 100644
--- a/tests/integration/api_activitypub_repository_test.go
+++ b/tests/integration/api_activitypub_repository_test.go
@@ -91,7 +91,7 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) {
 				`"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`)
 			fmt.Fprint(res, responseBody)
 		})
-	federatedRoutes.HandleFunc("/api/v1/activitypub/user-id/2",
+	federatedRoutes.HandleFunc("/api/v1/activitypub/user-id/15",
 		func(res http.ResponseWriter, req *http.Request) {
 			// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2
 			responseBody := fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],` +
@@ -132,7 +132,7 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) {
 		activity := []byte(fmt.Sprintf(
 			`{"type":"Like",`+
 				`"startTime":"%s",`+
-				`"actor":"%s/api/v1/activitypub/user-id/2",`+
+				`"actor":"%s/api/v1/activitypub/user-id/15",`+
 				`"object":"%s/api/v1/activitypub/repository-id/%v"}`,
 			time.Now().UTC().Format(time.RFC3339),
 			federatedSrv.URL, srv.URL, repositoryID))
@@ -142,7 +142,9 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, http.StatusNoContent, resp.StatusCode)
 
-		unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"})
+		federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"})
+		federatedUser := unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "15", FederationHostID: federationHost.ID})
+		unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID})
 	})
 }