From b491b2104f83ee8fc4956c099c427b339291b3be Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Thu, 15 Aug 2024 23:59:01 +0800
Subject: [PATCH] Fix panic of ssh public key page after deletion of auth
 source (#31829)

Fix #31730

This PR rewrote the function `PublicKeysAreExternallyManaged` with a
simple test. The new function removed the loop to make it more readable.
---
 models/asymkey/ssh_key.go      | 23 +++++++----------------
 models/asymkey/ssh_key_test.go | 10 ++++++++++
 2 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go
index a409d8e841..7a18732c32 100644
--- a/models/asymkey/ssh_key.go
+++ b/models/asymkey/ssh_key.go
@@ -229,35 +229,26 @@ func UpdatePublicKeyUpdated(ctx context.Context, id int64) error {
 
 // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
 func PublicKeysAreExternallyManaged(ctx context.Context, keys []*PublicKey) ([]bool, error) {
-	sources := make([]*auth.Source, 0, 5)
+	sourceCache := make(map[int64]*auth.Source, len(keys))
 	externals := make([]bool, len(keys))
-keyloop:
+
 	for i, key := range keys {
 		if key.LoginSourceID == 0 {
 			externals[i] = false
-			continue keyloop
+			continue
 		}
 
-		var source *auth.Source
-
-	sourceloop:
-		for _, s := range sources {
-			if s.ID == key.LoginSourceID {
-				source = s
-				break sourceloop
-			}
-		}
-
-		if source == nil {
+		source, ok := sourceCache[key.LoginSourceID]
+		if !ok {
 			var err error
 			source, err = auth.GetSourceByID(ctx, key.LoginSourceID)
 			if err != nil {
 				if auth.IsErrSourceNotExist(err) {
 					externals[i] = false
-					sources[i] = &auth.Source{
+					sourceCache[key.LoginSourceID] = &auth.Source{
 						ID: key.LoginSourceID,
 					}
-					continue keyloop
+					continue
 				}
 				return nil, err
 			}
diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go
index d3e886b97f..18c23dc78c 100644
--- a/models/asymkey/ssh_key_test.go
+++ b/models/asymkey/ssh_key_test.go
@@ -12,6 +12,8 @@ import (
 	"strings"
 	"testing"
 
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/42wim/sshsig"
@@ -503,3 +505,11 @@ func runErr(t *testing.T, stdin []byte, args ...string) {
 		t.Fatal("expected error")
 	}
 }
+
+func Test_PublicKeysAreExternallyManaged(t *testing.T) {
+	key1 := unittest.AssertExistsAndLoadBean(t, &PublicKey{ID: 1})
+	externals, err := PublicKeysAreExternallyManaged(db.DefaultContext, []*PublicKey{key1})
+	assert.NoError(t, err)
+	assert.Len(t, externals, 1)
+	assert.False(t, externals[0])
+}