Skip to content

Commit 3ed601d

Browse files
switch to use user_settings table per feedback
1 parent 2f3f2d1 commit 3ed601d

File tree

5 files changed

+74
-51
lines changed

5 files changed

+74
-51
lines changed

models/migrations/migrations.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,6 @@ func prepareMigrationTasks() []*migration {
386386

387387
// Gitea 1.24.0 ends at migration ID number 320 (database version 321)
388388
newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs),
389-
newMigration(322, "Add Mirror SSH keypair table", v1_25.AddUserSSHKeypairTable),
390389
}
391390
return preparedMigrations
392391
}

models/migrations/v1_25/v322.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

models/repo/mirror_ssh_keypair.go

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,51 @@ import (
1616
user_model "code.gitea.io/gitea/models/user"
1717
"code.gitea.io/gitea/modules/secret"
1818
"code.gitea.io/gitea/modules/setting"
19-
"code.gitea.io/gitea/modules/timeutil"
2019
"code.gitea.io/gitea/modules/util"
2120

2221
"golang.org/x/crypto/ssh"
2322
)
2423

2524
// UserSSHKeypair represents an SSH keypair for repository mirroring
2625
type UserSSHKeypair struct {
27-
ID int64 `xorm:"pk autoincr"`
28-
OwnerID int64 `xorm:"INDEX NOT NULL"`
29-
PrivateKeyEncrypted string `xorm:"TEXT NOT NULL"`
30-
PublicKey string `xorm:"TEXT NOT NULL"`
31-
Fingerprint string `xorm:"VARCHAR(255) UNIQUE NOT NULL"`
32-
CreatedUnix timeutil.TimeStamp `xorm:"created"`
33-
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
26+
OwnerID int64
27+
PrivateKeyEncrypted string
28+
PublicKey string
29+
Fingerprint string
3430
}
3531

36-
func init() {
37-
db.RegisterModel(new(UserSSHKeypair))
38-
}
39-
40-
// GetUserSSHKeypairByOwner gets the most recent SSH keypair for the given owner
32+
// GetUserSSHKeypairByOwner gets the SSH keypair for the given owner
4133
func GetUserSSHKeypairByOwner(ctx context.Context, ownerID int64) (*UserSSHKeypair, error) {
42-
keypair := &UserSSHKeypair{}
43-
has, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).
44-
Desc("created_unix").Get(keypair)
34+
settings, err := user_model.GetSettings(ctx, ownerID, []string{
35+
user_model.UserSSHMirrorPrivPem,
36+
user_model.UserSSHMirrorPubPem,
37+
user_model.UserSSHMirrorFingerprint,
38+
})
4539
if err != nil {
4640
return nil, err
4741
}
48-
if !has {
42+
if len(settings) == 0 {
4943
return nil, util.NewNotExistErrorf("SSH keypair does not exist for owner %d", ownerID)
5044
}
45+
46+
keypair := &UserSSHKeypair{
47+
OwnerID: ownerID,
48+
}
49+
50+
if privSetting, exists := settings[user_model.UserSSHMirrorPrivPem]; exists {
51+
keypair.PrivateKeyEncrypted = privSetting.SettingValue
52+
}
53+
if pubSetting, exists := settings[user_model.UserSSHMirrorPubPem]; exists {
54+
keypair.PublicKey = pubSetting.SettingValue
55+
}
56+
if fpSetting, exists := settings[user_model.UserSSHMirrorFingerprint]; exists {
57+
keypair.Fingerprint = fpSetting.SettingValue
58+
}
59+
60+
if keypair.PrivateKeyEncrypted == "" || keypair.PublicKey == "" || keypair.Fingerprint == "" {
61+
return nil, util.NewNotExistErrorf("SSH keypair incomplete for owner %d", ownerID)
62+
}
63+
5164
return keypair, nil
5265
}
5366

@@ -73,14 +86,30 @@ func CreateUserSSHKeypair(ctx context.Context, ownerID int64) (*UserSSHKeypair,
7386
return nil, fmt.Errorf("failed to encrypt private key: %w", err)
7487
}
7588

89+
err = db.WithTx(ctx, func(ctx context.Context) error {
90+
if err := user_model.SetUserSetting(ctx, ownerID, user_model.UserSSHMirrorPrivPem, privateKeyEncrypted); err != nil {
91+
return fmt.Errorf("failed to save private key: %w", err)
92+
}
93+
if err := user_model.SetUserSetting(ctx, ownerID, user_model.UserSSHMirrorPubPem, publicKeyStr); err != nil {
94+
return fmt.Errorf("failed to save public key: %w", err)
95+
}
96+
if err := user_model.SetUserSetting(ctx, ownerID, user_model.UserSSHMirrorFingerprint, fingerprintStr); err != nil {
97+
return fmt.Errorf("failed to save fingerprint: %w", err)
98+
}
99+
return nil
100+
})
101+
if err != nil {
102+
return nil, err
103+
}
104+
76105
keypair := &UserSSHKeypair{
77106
OwnerID: ownerID,
78107
PrivateKeyEncrypted: privateKeyEncrypted,
79108
PublicKey: publicKeyStr,
80109
Fingerprint: fingerprintStr,
81110
}
82111

83-
return keypair, db.Insert(ctx, keypair)
112+
return keypair, nil
84113
}
85114

86115
// GetDecryptedPrivateKey returns the decrypted private key
@@ -115,12 +144,29 @@ func (k *UserSSHKeypair) GetPublicKeyWithComment(ctx context.Context) (string, e
115144

116145
// DeleteUserSSHKeypair deletes an SSH keypair
117146
func DeleteUserSSHKeypair(ctx context.Context, ownerID int64) error {
118-
_, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Delete(&UserSSHKeypair{})
119-
return err
147+
return db.WithTx(ctx, func(ctx context.Context) error {
148+
if err := user_model.DeleteUserSetting(ctx, ownerID, user_model.UserSSHMirrorPrivPem); err != nil {
149+
return err
150+
}
151+
if err := user_model.DeleteUserSetting(ctx, ownerID, user_model.UserSSHMirrorPubPem); err != nil {
152+
return err
153+
}
154+
return user_model.DeleteUserSetting(ctx, ownerID, user_model.UserSSHMirrorFingerprint)
155+
})
120156
}
121157

122158
// RegenerateUserSSHKeypair regenerates an SSH keypair for the given owner
123159
func RegenerateUserSSHKeypair(ctx context.Context, ownerID int64) (*UserSSHKeypair, error) {
124-
// TODO: This creates a new one old ones will be garbage collected later, as the user may accidentally regenerate
125-
return CreateUserSSHKeypair(ctx, ownerID)
160+
var keypair *UserSSHKeypair
161+
err := db.WithTx(ctx, func(ctx context.Context) error {
162+
_ = DeleteUserSSHKeypair(ctx, ownerID)
163+
164+
newKeypair, err := CreateUserSSHKeypair(ctx, ownerID)
165+
if err != nil {
166+
return err
167+
}
168+
keypair = newKeypair
169+
return nil
170+
})
171+
return keypair, err
126172
}

models/repo/mirror_ssh_keypair_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ func TestUserSSHKeypair(t *testing.T) {
2929
assert.NotEmpty(t, keypair.PublicKey)
3030
assert.NotEmpty(t, keypair.PrivateKeyEncrypted)
3131
assert.NotEmpty(t, keypair.Fingerprint)
32-
assert.Positive(t, keypair.CreatedUnix)
33-
assert.Positive(t, keypair.UpdatedUnix)
3432

3533
// Verify the public key is in SSH format
3634
assert.Contains(t, keypair.PublicKey, "ssh-ed25519")
@@ -54,7 +52,7 @@ func TestUserSSHKeypair(t *testing.T) {
5452
// Test retrieving the keypair
5553
retrieved, err := repo_model.GetUserSSHKeypairByOwner(db.DefaultContext, 3)
5654
require.NoError(t, err)
57-
assert.Equal(t, created.ID, retrieved.ID)
55+
assert.Equal(t, created.OwnerID, retrieved.OwnerID)
5856
assert.Equal(t, created.PublicKey, retrieved.PublicKey)
5957
assert.Equal(t, created.Fingerprint, retrieved.Fingerprint)
6058

@@ -128,7 +126,7 @@ func TestUserSSHKeypairConcurrency(t *testing.T) {
128126

129127
// Test concurrent creation of keypairs to ensure no race conditions
130128
t.Run("ConcurrentCreation", func(t *testing.T) {
131-
ctx := t.Context()
129+
ctx := db.DefaultContext
132130
results := make(chan error, 10)
133131

134132
// Start multiple goroutines creating keypairs for different owners

models/user/setting_options.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ const (
2626
SettingEmailNotificationGiteaActionsAll = "all"
2727
SettingEmailNotificationGiteaActionsFailureOnly = "failure-only" // Default for actions email preference
2828
SettingEmailNotificationGiteaActionsDisabled = "disabled"
29+
30+
UserSSHMirrorPrivPem = "ssh_mirror.priv_pem"
31+
UserSSHMirrorPubPem = "ssh_mirror.pub_pem"
32+
UserSSHMirrorFingerprint = "ssh_mirror.fingerprint"
2933
)

0 commit comments

Comments
 (0)