Skip to content

Commit c289b61

Browse files
authored
Merge branch 'main' into lunny/fix_shutdown_issue
2 parents 8956513 + 8085c75 commit c289b61

File tree

92 files changed

+1410
-872
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+1410
-872
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.GetLFSAuthTokenWithBearer(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.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
298275
if err != nil {
299276
return err
300277
}

custom/conf/app.example.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,12 @@ LEVEL = Info
25412541
;; * no-sanitizer: Disable the sanitizer and render the content inside current page. It's **insecure** and may lead to XSS attack if the content contains malicious code.
25422542
;; * iframe: Render the content in a separate standalone page and embed it into current page by iframe. The iframe is in sandbox mode with same-origin disabled, and the JS code are safely isolated from parent page.
25432543
;RENDER_CONTENT_MODE=sanitized
2544+
;;
2545+
;; Whether post-process the rendered HTML content, including:
2546+
;; resolve relative links and image sources, recognizing issue/commit references, escaping invisible characters,
2547+
;; mentioning users, rendering permlink code blocks, replacing emoji shorthands, etc.
2548+
;; By default, this is true when RENDER_CONTENT_MODE is `sanitized`, otherwise false.
2549+
;NEED_POST_PROCESS=false
25442550

25452551
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25462552
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

models/admin/task.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
repo_model "code.gitea.io/gitea/models/repo"
1212
user_model "code.gitea.io/gitea/models/user"
1313
"code.gitea.io/gitea/modules/json"
14+
"code.gitea.io/gitea/modules/log"
1415
"code.gitea.io/gitea/modules/migration"
1516
"code.gitea.io/gitea/modules/secret"
1617
"code.gitea.io/gitea/modules/setting"
@@ -123,17 +124,17 @@ func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
123124
// decrypt credentials
124125
if opts.CloneAddrEncrypted != "" {
125126
if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
126-
return nil, err
127+
log.Error("Unable to decrypt CloneAddr, maybe SECRET_KEY is wrong: %v", err)
127128
}
128129
}
129130
if opts.AuthPasswordEncrypted != "" {
130131
if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
131-
return nil, err
132+
log.Error("Unable to decrypt AuthPassword, maybe SECRET_KEY is wrong: %v", err)
132133
}
133134
}
134135
if opts.AuthTokenEncrypted != "" {
135136
if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
136-
return nil, err
137+
log.Error("Unable to decrypt AuthToken, maybe SECRET_KEY is wrong: %v", err)
137138
}
138139
}
139140

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: 42 additions & 31 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,17 +34,45 @@ 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) {
5438
sb := &strings.Builder{}
55-
_ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]any{
39+
_, err := writeAuthorizedStringForKey(key, sb)
40+
return sb.String(), err
41+
}
42+
43+
// WriteAuthorizedStringForValidKey writes the authorized key for the provided key. If the key is invalid, it does nothing.
44+
func WriteAuthorizedStringForValidKey(key *PublicKey, w io.Writer) error {
45+
validKey, err := writeAuthorizedStringForKey(key, w)
46+
if !validKey {
47+
log.Debug("WriteAuthorizedStringForValidKey: key %s is not valid: %v", key, err)
48+
return nil
49+
}
50+
return err
51+
}
52+
53+
func writeAuthorizedStringForKey(key *PublicKey, w io.Writer) (keyValid bool, err error) {
54+
const tpl = AuthorizedStringCommentPrefix + "\n" + `command=%s,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict %s %s` + "\n"
55+
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
56+
if err != nil {
57+
return false, err
58+
}
59+
// now the key is valid, the code below could only return template/IO related errors
60+
sbCmd := &strings.Builder{}
61+
err = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sbCmd, map[string]any{
5662
"AppPath": util.ShellEscape(setting.AppPath),
5763
"AppWorkPath": util.ShellEscape(setting.AppWorkPath),
5864
"CustomConf": util.ShellEscape(setting.CustomConf),
5965
"CustomPath": util.ShellEscape(setting.CustomPath),
6066
"Key": key,
6167
})
62-
63-
return fmt.Sprintf(tplPublicKey, util.ShellEscape(sb.String()), key.Content)
68+
if err != nil {
69+
return true, err
70+
}
71+
sshCommandEscaped := util.ShellEscape(sbCmd.String())
72+
sshKeyMarshalled := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pubKey)))
73+
sshKeyComment := fmt.Sprintf("user-%d", key.OwnerID)
74+
_, err = fmt.Fprintf(w, tpl, sshCommandEscaped, sshKeyMarshalled, sshKeyComment)
75+
return true, err
6476
}
6577

6678
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
@@ -112,18 +124,17 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
112124
if key.Type == KeyTypePrincipal {
113125
continue
114126
}
115-
if _, err = f.WriteString(key.AuthorizedString()); err != nil {
127+
if err = WriteAuthorizedStringForValidKey(key, f); err != nil {
116128
return err
117129
}
118130
}
119131
return nil
120132
}
121133

122134
// RegeneratePublicKeys regenerates the authorized_keys file
123-
func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
135+
func RegeneratePublicKeys(ctx context.Context, t io.Writer) error {
124136
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())
126-
return err
137+
return WriteAuthorizedStringForValidKey(bean.(*PublicKey), t)
127138
}); err != nil {
128139
return err
129140
}
@@ -144,11 +155,11 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
144155
scanner := bufio.NewScanner(f)
145156
for scanner.Scan() {
146157
line := scanner.Text()
147-
if strings.HasPrefix(line, tplCommentPrefix) {
158+
if strings.HasPrefix(line, AuthorizedStringCommentPrefix) {
148159
scanner.Scan()
149160
continue
150161
}
151-
_, err = t.WriteString(line + "\n")
162+
_, err = io.WriteString(t, line+"\n")
152163
if err != nil {
153164
return err
154165
}

models/auth/twofactor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ func (t *TwoFactor) SetSecret(secretString string) error {
111111
func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
112112
decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
113113
if err != nil {
114-
return false, err
114+
return false, fmt.Errorf("ValidateTOTP invalid base64: %w", err)
115115
}
116116
secretBytes, err := secret.AesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
117117
if err != nil {
118-
return false, err
118+
return false, fmt.Errorf("ValidateTOTP unable to decrypt (maybe SECRET_KEY is wrong): %w", err)
119119
}
120120
secretStr := string(secretBytes)
121121
return totp.Validate(passcode, secretStr), nil

models/git/lfs.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"fmt"
99

1010
"code.gitea.io/gitea/models/db"
11-
"code.gitea.io/gitea/models/perm"
1211
repo_model "code.gitea.io/gitea/models/repo"
1312
"code.gitea.io/gitea/models/unit"
1413
user_model "code.gitea.io/gitea/models/user"
@@ -42,30 +41,6 @@ func (err ErrLFSLockNotExist) Unwrap() error {
4241
return util.ErrNotExist
4342
}
4443

45-
// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error.
46-
type ErrLFSUnauthorizedAction struct {
47-
RepoID int64
48-
UserName string
49-
Mode perm.AccessMode
50-
}
51-
52-
// IsErrLFSUnauthorizedAction checks if an error is a ErrLFSUnauthorizedAction.
53-
func IsErrLFSUnauthorizedAction(err error) bool {
54-
_, ok := err.(ErrLFSUnauthorizedAction)
55-
return ok
56-
}
57-
58-
func (err ErrLFSUnauthorizedAction) Error() string {
59-
if err.Mode == perm.AccessModeWrite {
60-
return fmt.Sprintf("User %s doesn't have write access for lfs lock [rid: %d]", err.UserName, err.RepoID)
61-
}
62-
return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID)
63-
}
64-
65-
func (err ErrLFSUnauthorizedAction) Unwrap() error {
66-
return util.ErrPermissionDenied
67-
}
68-
6944
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
7045
type ErrLFSLockAlreadyExist struct {
7146
RepoID int64
@@ -93,12 +68,6 @@ type ErrLFSFileLocked struct {
9368
UserName string
9469
}
9570

96-
// IsErrLFSFileLocked checks if an error is a ErrLFSFileLocked.
97-
func IsErrLFSFileLocked(err error) bool {
98-
_, ok := err.(ErrLFSFileLocked)
99-
return ok
100-
}
101-
10271
func (err ErrLFSFileLocked) Error() string {
10372
return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path)
10473
}

models/git/lfs_lock.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ import (
1111
"time"
1212

1313
"code.gitea.io/gitea/models/db"
14-
"code.gitea.io/gitea/models/perm"
15-
access_model "code.gitea.io/gitea/models/perm/access"
1614
repo_model "code.gitea.io/gitea/models/repo"
17-
"code.gitea.io/gitea/models/unit"
1815
user_model "code.gitea.io/gitea/models/user"
1916
"code.gitea.io/gitea/modules/setting"
2017
"code.gitea.io/gitea/modules/util"
@@ -71,10 +68,6 @@ func (l *LFSLock) LoadOwner(ctx context.Context) error {
7168
// CreateLFSLock creates a new lock.
7269
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
7370
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
74-
if err := CheckLFSAccessForRepo(ctx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
75-
return nil, err
76-
}
77-
7871
lock.Path = util.PathJoinRel(lock.Path)
7972
lock.RepoID = repo.ID
8073

@@ -165,10 +158,6 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
165158
return nil, err
166159
}
167160

168-
if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
169-
return nil, err
170-
}
171-
172161
if !force && u.ID != lock.OwnerID {
173162
return nil, errors.New("user doesn't own lock and force flag is not set")
174163
}
@@ -180,22 +169,3 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
180169
return lock, nil
181170
})
182171
}
183-
184-
// CheckLFSAccessForRepo check needed access mode base on action
185-
func CheckLFSAccessForRepo(ctx context.Context, ownerID int64, repo *repo_model.Repository, mode perm.AccessMode) error {
186-
if ownerID == 0 {
187-
return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode}
188-
}
189-
u, err := user_model.GetUserByID(ctx, ownerID)
190-
if err != nil {
191-
return err
192-
}
193-
perm, err := access_model.GetUserRepoPermission(ctx, repo, u)
194-
if err != nil {
195-
return err
196-
}
197-
if !perm.CanAccess(mode, unit.TypeCode) {
198-
return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), mode}
199-
}
200-
return nil
201-
}

models/git/protected_branch.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package git
55

66
import (
77
"context"
8-
"errors"
98
"fmt"
109
"slices"
1110
"strings"
@@ -25,7 +24,7 @@ import (
2524
"xorm.io/builder"
2625
)
2726

28-
var ErrBranchIsProtected = errors.New("branch is protected")
27+
var ErrBranchIsProtected = util.ErrorWrap(util.ErrPermissionDenied, "branch is protected")
2928

3029
// ProtectedBranch struct
3130
type ProtectedBranch struct {

models/organization/org.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User)
429429
return true
430430
}
431431

432+
if !setting.Service.RequireSignInViewStrict && orgOrUser.Visibility == structs.VisibleTypePublic {
433+
return true
434+
}
435+
432436
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
433437
return false
434438
}

0 commit comments

Comments
 (0)