Skip to content

Commit be8827b

Browse files
committed
fix
1 parent 66ee8f3 commit be8827b

File tree

15 files changed

+354
-228
lines changed

15 files changed

+354
-228
lines changed

cmd/serv.go

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"path/filepath"
1414
"strconv"
1515
"strings"
16-
"time"
1716
"unicode"
1817

1918
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -32,7 +31,6 @@ import (
3231
"code.gitea.io/gitea/modules/setting"
3332
"code.gitea.io/gitea/services/lfs"
3433

35-
"github.com/golang-jwt/jwt/v5"
3634
"github.com/kballard/go-shellquote"
3735
"github.com/urfave/cli/v3"
3836
)
@@ -133,27 +131,6 @@ func getAccessMode(verb, lfsVerb string) perm.AccessMode {
133131
return perm.AccessModeNone
134132
}
135133

136-
func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServCommandResults) (string, error) {
137-
now := time.Now()
138-
claims := lfs.Claims{
139-
RegisteredClaims: jwt.RegisteredClaims{
140-
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
141-
NotBefore: jwt.NewNumericDate(now),
142-
},
143-
RepoID: results.RepoID,
144-
Op: lfsVerb,
145-
UserID: results.UserID,
146-
}
147-
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
148-
149-
// Sign and get the complete encoded token as a string using the secret
150-
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
151-
if err != nil {
152-
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
153-
}
154-
return "Bearer " + tokenString, nil
155-
}
156-
157134
func runServ(ctx context.Context, c *cli.Command) error {
158135
// FIXME: This needs to internationalised
159136
setup(ctx, c.Bool("debug"))
@@ -283,7 +260,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
283260

284261
// LFS SSH protocol
285262
if verb == git.CmdVerbLfsTransfer {
286-
token, err := getLFSAuthToken(ctx, lfsVerb, results)
263+
token, err := lfs.GetLFSAuthToken(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
287264
if err != nil {
288265
return err
289266
}
@@ -294,7 +271,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
294271
if verb == git.CmdVerbLfsAuthenticate {
295272
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
296273

297-
token, err := getLFSAuthToken(ctx, lfsVerb, results)
274+
token, err := lfs.GetLFSAuthToken(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
298275
if err != nil {
299276
return err
300277
}

models/asymkey/ssh_key.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,6 @@ func (key *PublicKey) OmitEmail() string {
6767
return strings.Join(strings.Split(key.Content, " ")[:2], " ")
6868
}
6969

70-
// AuthorizedString returns formatted public key string for authorized_keys file.
71-
//
72-
// TODO: Consider dropping this function
73-
func (key *PublicKey) AuthorizedString() string {
74-
return AuthorizedStringForKey(key)
75-
}
76-
7770
func addKey(ctx context.Context, key *PublicKey) (err error) {
7871
if len(key.Fingerprint) == 0 {
7972
key.Fingerprint, err = CalcFingerprint(key.Content)

models/asymkey/ssh_key_authorized_keys.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,14 @@ import (
1717
"code.gitea.io/gitea/modules/log"
1818
"code.gitea.io/gitea/modules/setting"
1919
"code.gitea.io/gitea/modules/util"
20-
)
2120

22-
// _____ __ .__ .__ .___
23-
// / _ \ __ ___/ |_| |__ ___________|__|_______ ____ __| _/
24-
// / /_\ \| | \ __\ | \ / _ \_ __ \ \___ // __ \ / __ |
25-
// / | \ | /| | | Y ( <_> ) | \/ |/ /\ ___// /_/ |
26-
// \____|__ /____/ |__| |___| /\____/|__| |__/_____ \\___ >____ |
27-
// \/ \/ \/ \/ \/
28-
// ____ __.
29-
// | |/ _|____ ___.__. ______
30-
// | <_/ __ < | |/ ___/
31-
// | | \ ___/\___ |\___ \
32-
// |____|__ \___ > ____/____ >
33-
// \/ \/\/ \/
34-
//
35-
// This file contains functions for creating authorized_keys files
36-
//
37-
// There is a dependence on the database within RegeneratePublicKeys however most of these functions probably belong in a module
38-
39-
const (
40-
tplCommentPrefix = `# gitea public key`
41-
tplPublicKey = tplCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s` + "\n"
21+
"golang.org/x/crypto/ssh"
4222
)
4323

24+
// AuthorizedStringCommentPrefix is a magic tag
25+
// some functions like RegeneratePublicKeys needs this tag to skip the keys generated by Gitea, while keep other keys
26+
const AuthorizedStringCommentPrefix = `# gitea public key`
27+
4428
var sshOpLocker sync.Mutex
4529

4630
func WithSSHOpLocker(f func() error) error {
@@ -50,7 +34,8 @@ func WithSSHOpLocker(f func() error) error {
5034
}
5135

5236
// AuthorizedStringForKey creates the authorized keys string appropriate for the provided key
53-
func AuthorizedStringForKey(key *PublicKey) string {
37+
func AuthorizedStringForKey(key *PublicKey) (string, error) {
38+
const tpl = AuthorizedStringCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s %s` + "\n"
5439
sb := &strings.Builder{}
5540
_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]any{
5641
"AppPath": util.ShellEscape(setting.AppPath),
@@ -60,7 +45,14 @@ func AuthorizedStringForKey(key *PublicKey) string {
6045
"Key": key,
6146
})
6247

63-
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
48+
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
49+
if err != nil {
50+
return "", fmt.Errorf("invalid public key: %w", err)
51+
}
52+
sshCommandEscaped := util.ShellEscape(sb.String())
53+
sshKeyMarshalled := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
54+
sshKeyComment := fmt.Sprintf("user-%d", key.OwnerID)
55+
return fmt.Sprintf(tpl, sshCommandEscaped, sshKeyMarshalled, sshKeyComment), nil
6456
}
6557

6658
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
@@ -112,7 +104,12 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
112104
if key.Type == KeyTypePrincipal {
113105
continue
114106
}
115-
if _, err = f.WriteString(key.AuthorizedString()); err != nil {
107+
authorizedString, err := AuthorizedStringForKey(key)
108+
if err != nil {
109+
log.Debug("AuthorizedStringForKey(%s): %v", key, err)
110+
continue
111+
}
112+
if _, err = f.WriteString(authorizedString); err != nil {
116113
return err
117114
}
118115
}
@@ -122,7 +119,13 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
122119
// RegeneratePublicKeys regenerates the authorized_keys file
123120
func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
124121
if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) {
125-
_, err = t.WriteString((bean.(*PublicKey)).AuthorizedString())
122+
key := bean.(*PublicKey)
123+
authorizedString, err := AuthorizedStringForKey(key)
124+
if err != nil {
125+
log.Debug("AuthorizedStringForKey(%s): %v", key, err)
126+
return nil
127+
}
128+
_, err = t.WriteString(authorizedString)
126129
return err
127130
}); err != nil {
128131
return err
@@ -144,7 +147,7 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
144147
scanner := bufio.NewScanner(f)
145148
for scanner.Scan() {
146149
line := scanner.Text()
147-
if strings.HasPrefix(line, tplCommentPrefix) {
150+
if strings.HasPrefix(line, AuthorizedStringCommentPrefix) {
148151
scanner.Scan()
149152
continue
150153
}

models/repo/upload.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,9 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
127127

128128
for _, upload := range uploads {
129129
localPath := upload.LocalPath()
130-
isFile, err := util.IsFile(localPath)
131-
if err != nil {
132-
log.Error("Unable to check if %s is a file. Error: %v", localPath, err)
133-
}
134-
if !isFile {
135-
continue
136-
}
137-
138130
if err := util.Remove(localPath); err != nil {
139-
return fmt.Errorf("remove upload: %w", err)
131+
// just continue, don't fail the whole operation if a file is missing (removed by others)
132+
log.Error("unable to remove upload file %s: %v", localPath, err)
140133
}
141134
}
142135

modules/git/hook.go

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,16 @@ func GetHook(repoPath, name string) (*Hook, error) {
4747
name: name,
4848
path: filepath.Join(repoPath, filepath.Join("hooks", name+".d", name)),
4949
}
50-
isFile, err := util.IsFile(h.path)
51-
if err != nil {
52-
return nil, err
53-
}
54-
if isFile {
55-
data, err := os.ReadFile(h.path)
56-
if err != nil {
57-
return nil, err
58-
}
50+
if data, err := os.ReadFile(h.path); err == nil {
5951
h.IsActive = true
6052
h.Content = string(data)
6153
return h, nil
54+
} else if !os.IsNotExist(err) {
55+
return nil, err
6256
}
6357

6458
samplePath := filepath.Join(repoPath, "hooks", name+".sample")
65-
isFile, err = util.IsFile(samplePath)
66-
if err != nil {
67-
return nil, err
68-
}
69-
if isFile {
70-
data, err := os.ReadFile(samplePath)
71-
if err != nil {
72-
return nil, err
73-
}
59+
if data, err := os.ReadFile(samplePath); err == nil {
7460
h.Sample = string(data)
7561
}
7662
return h, nil

modules/setting/config_provider.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ func NewConfigProviderFromFile(file string) (ConfigProvider, error) {
202202
loadedFromEmpty := true
203203

204204
if file != "" {
205-
isFile, err := util.IsFile(file)
205+
isExist, err := util.IsExist(file)
206206
if err != nil {
207-
return nil, fmt.Errorf("unable to check if %q is a file. Error: %v", file, err)
207+
return nil, fmt.Errorf("unable to check if %q exists: %v", file, err)
208208
}
209-
if isFile {
209+
if isExist {
210210
if err = cfg.Append(file); err != nil {
211211
return nil, fmt.Errorf("failed to load config file %q: %v", file, err)
212212
}

modules/util/path.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,22 @@ func IsDir(dir string) (bool, error) {
115115
return false, err
116116
}
117117

118-
// IsFile returns true if given path is a file,
119-
// or returns false when it's a directory or does not exist.
120-
func IsFile(filePath string) (bool, error) {
121-
f, err := os.Stat(filePath)
118+
func IsRegularFile(filePath string) (bool, error) {
119+
f, err := os.Lstat(filePath)
122120
if err == nil {
123-
return !f.IsDir(), nil
124-
}
125-
if os.IsNotExist(err) {
126-
return false, nil
121+
return f.Mode().IsRegular(), nil
127122
}
128123
return false, err
129124
}
130125

126+
func IsRegularFileOrNotExist(filePath string) bool {
127+
b, err := IsRegularFile(filePath)
128+
if b || os.IsNotExist(err) {
129+
return true
130+
}
131+
return false
132+
}
133+
131134
// IsExist checks whether a file or directory exists.
132135
// It returns false when the file or directory does not exist.
133136
func IsExist(path string) (bool, error) {

routers/private/key.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func UpdatePublicKeyInRepo(ctx *context.PrivateContext) {
4545
ctx.PlainText(http.StatusOK, "success")
4646
}
4747

48-
// AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part)
48+
// AuthorizedPublicKeyByContent searches content as prefix (without e-mail part)
4949
// and returns public key found.
5050
func AuthorizedPublicKeyByContent(ctx *context.PrivateContext) {
5151
content := ctx.FormString("content")
@@ -57,5 +57,14 @@ func AuthorizedPublicKeyByContent(ctx *context.PrivateContext) {
5757
})
5858
return
5959
}
60-
ctx.PlainText(http.StatusOK, publicKey.AuthorizedString())
60+
61+
authorizedString, err := asymkey_model.AuthorizedStringForKey(publicKey)
62+
if err != nil {
63+
ctx.JSON(http.StatusInternalServerError, private.Response{
64+
Err: err.Error(),
65+
UserMsg: "invalid public key",
66+
})
67+
return
68+
}
69+
ctx.PlainText(http.StatusOK, authorizedString)
6170
}

services/asymkey/ssh_key_authorized_principals.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ import (
2525
// There is a dependence on the database within RewriteAllPrincipalKeys & RegeneratePrincipalKeys
2626
// The sshOpLocker is used from ssh_key_authorized_keys.go
2727

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

3330
// RewriteAllPrincipalKeys removes any authorized principal and rewrite all keys from database again.
3431
// Note: db.GetEngine(ctx).Iterate does not get latest data after insert/delete, so we have to call this function
@@ -92,7 +89,13 @@ func rewriteAllPrincipalKeys(ctx context.Context) error {
9289

9390
func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
9491
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())
92+
key := bean.(*asymkey_model.PublicKey)
93+
authorizedString, err := asymkey_model.AuthorizedStringForKey(key)
94+
if err != nil {
95+
log.Debug("AuthorizedStringForKey(%s): %v", key, err)
96+
return nil
97+
}
98+
_, err = t.WriteString(authorizedString)
9699
return err
97100
}); err != nil {
98101
return err
@@ -114,7 +117,7 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
114117
scanner := bufio.NewScanner(f)
115118
for scanner.Scan() {
116119
line := scanner.Text()
117-
if strings.HasPrefix(line, tplCommentPrefix) {
120+
if strings.HasPrefix(line, asymkey_model.AuthorizedStringCommentPrefix) {
118121
scanner.Scan()
119122
continue
120123
}

services/doctor/authorizedkeys.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
asymkey_service "code.gitea.io/gitea/services/asymkey"
2121
)
2222

23-
const tplCommentPrefix = `# gitea public key`
24-
2523
func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) error {
2624
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
2725
return nil
@@ -47,7 +45,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
4745
scanner := bufio.NewScanner(f)
4846
for scanner.Scan() {
4947
line := scanner.Text()
50-
if strings.HasPrefix(line, tplCommentPrefix) {
48+
if strings.HasPrefix(line, asymkey_model.AuthorizedStringCommentPrefix) {
5149
continue
5250
}
5351
linesInAuthorizedKeys.Add(line)
@@ -67,7 +65,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
6765
scanner = bufio.NewScanner(regenerated)
6866
for scanner.Scan() {
6967
line := scanner.Text()
70-
if strings.HasPrefix(line, tplCommentPrefix) {
68+
if strings.HasPrefix(line, asymkey_model.AuthorizedStringCommentPrefix) {
7169
continue
7270
}
7371
if linesInAuthorizedKeys.Contains(line) {

0 commit comments

Comments
 (0)