Skip to content

Commit e2277d0

Browse files
authored
Move some asymkey functions to service layer (#28894)
After the moving, all models will not depend on `util.Rename` so that I can do next step refactoring.
1 parent c337ff0 commit e2277d0

File tree

16 files changed

+176
-140
lines changed

16 files changed

+176
-140
lines changed

cmd/admin_regenerate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
package cmd
55

66
import (
7-
asymkey_model "code.gitea.io/gitea/models/asymkey"
87
"code.gitea.io/gitea/modules/graceful"
8+
asymkey_service "code.gitea.io/gitea/services/asymkey"
99
repo_service "code.gitea.io/gitea/services/repository"
1010

1111
"github.com/urfave/cli/v2"
@@ -42,5 +42,5 @@ func runRegenerateKeys(_ *cli.Context) error {
4242
if err := initDB(ctx); err != nil {
4343
return err
4444
}
45-
return asymkey_model.RewriteAllPublicKeys(ctx)
45+
return asymkey_service.RewriteAllPublicKeys(ctx)
4646
}

models/asymkey/ssh_key_authorized_keys.go

Lines changed: 6 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"path/filepath"
1313
"strings"
1414
"sync"
15-
"time"
1615

1716
"code.gitea.io/gitea/models/db"
1817
"code.gitea.io/gitea/modules/log"
@@ -44,6 +43,12 @@ const (
4443

4544
var sshOpLocker sync.Mutex
4645

46+
func WithSSHOpLocker(f func() error) error {
47+
sshOpLocker.Lock()
48+
defer sshOpLocker.Unlock()
49+
return f()
50+
}
51+
4752
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
4853
func AuthorizedStringForKey(key *PublicKey) string {
4954
sb := &strings.Builder{}
@@ -114,65 +119,6 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
114119
return nil
115120
}
116121

117-
// RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again.
118-
// Note: db.GetEngine(ctx).Iterate does not get latest data after insert/delete, so we have to call this function
119-
// outside any session scope independently.
120-
func RewriteAllPublicKeys(ctx context.Context) error {
121-
// Don't rewrite key if internal server
122-
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
123-
return nil
124-
}
125-
126-
sshOpLocker.Lock()
127-
defer sshOpLocker.Unlock()
128-
129-
if setting.SSH.RootPath != "" {
130-
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
131-
// This of course doesn't guarantee that this is the right directory for authorized_keys
132-
// but at least if it's supposed to be this directory and it doesn't exist and we're the
133-
// right user it will at least be created properly.
134-
err := os.MkdirAll(setting.SSH.RootPath, 0o700)
135-
if err != nil {
136-
log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
137-
return err
138-
}
139-
}
140-
141-
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
142-
tmpPath := fPath + ".tmp"
143-
t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
144-
if err != nil {
145-
return err
146-
}
147-
defer func() {
148-
t.Close()
149-
if err := util.Remove(tmpPath); err != nil {
150-
log.Warn("Unable to remove temporary authorized keys file: %s: Error: %v", tmpPath, err)
151-
}
152-
}()
153-
154-
if setting.SSH.AuthorizedKeysBackup {
155-
isExist, err := util.IsExist(fPath)
156-
if err != nil {
157-
log.Error("Unable to check if %s exists. Error: %v", fPath, err)
158-
return err
159-
}
160-
if isExist {
161-
bakPath := fmt.Sprintf("%s_%d.gitea_bak", fPath, time.Now().Unix())
162-
if err = util.CopyFile(fPath, bakPath); err != nil {
163-
return err
164-
}
165-
}
166-
}
167-
168-
if err := RegeneratePublicKeys(ctx, t); err != nil {
169-
return err
170-
}
171-
172-
t.Close()
173-
return util.Rename(tmpPath, fPath)
174-
}
175-
176122
// RegeneratePublicKeys regenerates the authorized_keys file
177123
func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
178124
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {

models/asymkey/ssh_key_principals.go

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,11 @@ import (
99
"strings"
1010

1111
"code.gitea.io/gitea/models/db"
12-
"code.gitea.io/gitea/models/perm"
1312
user_model "code.gitea.io/gitea/models/user"
1413
"code.gitea.io/gitea/modules/setting"
1514
"code.gitea.io/gitea/modules/util"
1615
)
1716

18-
// AddPrincipalKey adds new principal to database and authorized_principals file.
19-
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
20-
dbCtx, committer, err := db.TxContext(ctx)
21-
if err != nil {
22-
return nil, err
23-
}
24-
defer committer.Close()
25-
26-
// Principals cannot be duplicated.
27-
has, err := db.GetEngine(dbCtx).
28-
Where("content = ? AND type = ?", content, KeyTypePrincipal).
29-
Get(new(PublicKey))
30-
if err != nil {
31-
return nil, err
32-
} else if has {
33-
return nil, ErrKeyAlreadyExist{0, "", content}
34-
}
35-
36-
key := &PublicKey{
37-
OwnerID: ownerID,
38-
Name: content,
39-
Content: content,
40-
Mode: perm.AccessModeWrite,
41-
Type: KeyTypePrincipal,
42-
LoginSourceID: authSourceID,
43-
}
44-
if err = db.Insert(dbCtx, key); err != nil {
45-
return nil, fmt.Errorf("addKey: %w", err)
46-
}
47-
48-
if err = committer.Commit(); err != nil {
49-
return nil, err
50-
}
51-
52-
committer.Close()
53-
54-
return key, RewriteAllPrincipalKeys(ctx)
55-
}
56-
5717
// CheckPrincipalKeyString strips spaces and returns an error if the given principal contains newlines
5818
func CheckPrincipalKeyString(ctx context.Context, user *user_model.User, content string) (_ string, err error) {
5919
if setting.SSH.Disabled {

routers/init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"runtime"
1010

1111
"code.gitea.io/gitea/models"
12-
asymkey_model "code.gitea.io/gitea/models/asymkey"
1312
authmodel "code.gitea.io/gitea/models/auth"
1413
"code.gitea.io/gitea/modules/cache"
1514
"code.gitea.io/gitea/modules/eventsource"
@@ -33,6 +32,7 @@ import (
3332
"code.gitea.io/gitea/routers/private"
3433
web_routers "code.gitea.io/gitea/routers/web"
3534
actions_service "code.gitea.io/gitea/services/actions"
35+
asymkey_service "code.gitea.io/gitea/services/asymkey"
3636
"code.gitea.io/gitea/services/auth"
3737
"code.gitea.io/gitea/services/auth/source/oauth2"
3838
"code.gitea.io/gitea/services/automerge"
@@ -94,7 +94,7 @@ func syncAppConfForGit(ctx context.Context) error {
9494
mustInitCtx(ctx, repo_service.SyncRepositoryHooks)
9595

9696
log.Info("re-write ssh public keys ...")
97-
mustInitCtx(ctx, asymkey_model.RewriteAllPublicKeys)
97+
mustInitCtx(ctx, asymkey_service.RewriteAllPublicKeys)
9898

9999
return system.AppState.Set(ctx, runtimeState)
100100
}

routers/web/user/setting/keys.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func KeysPost(ctx *context.Context) {
6262
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
6363
return
6464
}
65-
if _, err = asymkey_model.AddPrincipalKey(ctx, ctx.Doer.ID, content, 0); err != nil {
65+
if _, err = asymkey_service.AddPrincipalKey(ctx, ctx.Doer.ID, content, 0); err != nil {
6666
ctx.Data["HasPrincipalError"] = true
6767
switch {
6868
case asymkey_model.IsErrKeyAlreadyExist(err), asymkey_model.IsErrKeyNameAlreadyUsed(err):

services/asymkey/deploy_key.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"context"
88

99
"code.gitea.io/gitea/models"
10-
asymkey_model "code.gitea.io/gitea/models/asymkey"
1110
"code.gitea.io/gitea/models/db"
1211
user_model "code.gitea.io/gitea/models/user"
1312
)
@@ -27,5 +26,5 @@ func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error
2726
return err
2827
}
2928

30-
return asymkey_model.RewriteAllPublicKeys(ctx)
29+
return RewriteAllPublicKeys(ctx)
3130
}

services/asymkey/ssh_key.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func DeletePublicKey(ctx context.Context, doer *user_model.User, id int64) (err
4343
committer.Close()
4444

4545
if key.Type == asymkey_model.KeyTypePrincipal {
46-
return asymkey_model.RewriteAllPrincipalKeys(ctx)
46+
return RewriteAllPrincipalKeys(ctx)
4747
}
4848

49-
return asymkey_model.RewriteAllPublicKeys(ctx)
49+
return RewriteAllPublicKeys(ctx)
5050
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package asymkey
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
"time"
12+
13+
asymkey_model "code.gitea.io/gitea/models/asymkey"
14+
"code.gitea.io/gitea/modules/log"
15+
"code.gitea.io/gitea/modules/setting"
16+
"code.gitea.io/gitea/modules/util"
17+
)
18+
19+
// RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again.
20+
// Note: db.GetEngine(ctx).Iterate does not get latest data after insert/delete, so we have to call this function
21+
// outside any session scope independently.
22+
func RewriteAllPublicKeys(ctx context.Context) error {
23+
// Don't rewrite key if internal server
24+
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
25+
return nil
26+
}
27+
28+
return asymkey_model.WithSSHOpLocker(func() error {
29+
return rewriteAllPublicKeys(ctx)
30+
})
31+
}
32+
33+
func rewriteAllPublicKeys(ctx context.Context) error {
34+
if setting.SSH.RootPath != "" {
35+
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
36+
// This of course doesn't guarantee that this is the right directory for authorized_keys
37+
// but at least if it's supposed to be this directory and it doesn't exist and we're the
38+
// right user it will at least be created properly.
39+
err := os.MkdirAll(setting.SSH.RootPath, 0o700)
40+
if err != nil {
41+
log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
42+
return err
43+
}
44+
}
45+
46+
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
47+
tmpPath := fPath + ".tmp"
48+
t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
49+
if err != nil {
50+
return err
51+
}
52+
defer func() {
53+
t.Close()
54+
if err := util.Remove(tmpPath); err != nil {
55+
log.Warn("Unable to remove temporary authorized keys file: %s: Error: %v", tmpPath, err)
56+
}
57+
}()
58+
59+
if setting.SSH.AuthorizedKeysBackup {
60+
isExist, err := util.IsExist(fPath)
61+
if err != nil {
62+
log.Error("Unable to check if %s exists. Error: %v", fPath, err)
63+
return err
64+
}
65+
if isExist {
66+
bakPath := fmt.Sprintf("%s_%d.gitea_bak", fPath, time.Now().Unix())
67+
if err = util.CopyFile(fPath, bakPath); err != nil {
68+
return err
69+
}
70+
}
71+
}
72+
73+
if err := asymkey_model.RegeneratePublicKeys(ctx, t); err != nil {
74+
return err
75+
}
76+
77+
t.Close()
78+
return util.Rename(tmpPath, fPath)
79+
}

models/asymkey/ssh_key_authorized_principals.go renamed to services/asymkey/ssh_key_authorized_principals.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,22 @@ import (
1313
"strings"
1414
"time"
1515

16+
asymkey_model "code.gitea.io/gitea/models/asymkey"
1617
"code.gitea.io/gitea/models/db"
1718
"code.gitea.io/gitea/modules/log"
1819
"code.gitea.io/gitea/modules/setting"
1920
"code.gitea.io/gitea/modules/util"
2021
)
2122

22-
// _____ __ .__ .__ .___
23-
// / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/
24-
// / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ |
25-
// / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ |
26-
// \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ |
27-
// \/ \/ \/ \/ \/
28-
// __________ .__ .__ .__
29-
// \______ _______|__| ____ ____ |_____________ | | ______
30-
// | ___\_ __ | |/ \_/ ___\| \____ \__ \ | | / ___/
31-
// | | | | \| | | \ \___| | |_> / __ \| |__\___ \
32-
// |____| |__| |__|___| /\___ |__| __(____ |____/____ >
33-
// \/ \/ |__| \/ \/
34-
//
3523
// This file contains functions for creating authorized_principals files
3624
//
3725
// There is a dependence on the database within RewriteAllPrincipalKeys & RegeneratePrincipalKeys
3826
// The sshOpLocker is used from ssh_key_authorized_keys.go
3927

40-
const authorizedPrincipalsFile = "authorized_principals"
28+
const (
29+
authorizedPrincipalsFile = "authorized_principals"
30+
tplCommentPrefix = `# gitea public key`
31+
)
4132

4233
// RewriteAllPrincipalKeys removes any authorized principal and rewrite all keys from database again.
4334
// Note: db.GetEngine(ctx).Iterate does not get latest data after insert/delete, so we have to call this function
@@ -48,9 +39,12 @@ func RewriteAllPrincipalKeys(ctx context.Context) error {
4839
return nil
4940
}
5041

51-
sshOpLocker.Lock()
52-
defer sshOpLocker.Unlock()
42+
return asymkey_model.WithSSHOpLocker(func() error {
43+
return rewriteAllPrincipalKeys(ctx)
44+
})
45+
}
5346

47+
func rewriteAllPrincipalKeys(ctx context.Context) error {
5448
if setting.SSH.RootPath != "" {
5549
// First of ensure that the RootPath is present, and if not make it with 0700 permissions
5650
// This of course doesn't guarantee that this is the right directory for authorized_keys
@@ -97,8 +91,8 @@ func RewriteAllPrincipalKeys(ctx context.Context) error {
9791
}
9892

9993
func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
100-
if err := db.GetEngine(ctx).Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
101-
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
94+
if err := db.GetEngine(ctx).Where("type = ?", asymkey_model.KeyTypePrincipal).Iterate(new(asymkey_model.PublicKey), func(idx int, bean any) (err error) {
95+
_, err = t.WriteString((bean.(*asymkey_model.PublicKey)).AuthorizedString())
10296
return err
10397
}); err != nil {
10498
return err

0 commit comments

Comments
 (0)