Skip to content

Commit 62d750e

Browse files
lunnywxiaoguang
andauthored
Fix various permission & login related bugs (go-gitea#36002)
Permission & protection check: - Fix Delete Release permission check - Fix Update Pull Request with rebase branch protection check - Fix Issue Dependency permission check - Fix Delete Comment History ID check Information leaking: - Show unified message for non-existing user and invalid password - Fix go-gitea#35984 - Don't expose release draft to non-writer users. - Make API returns signature's email address instead of the user profile's. Auth & Login: - Avoid GCM OAuth2 attempt when OAuth2 is disabled - Fix go-gitea#35510 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent a60a8c6 commit 62d750e

File tree

18 files changed

+385
-61
lines changed

18 files changed

+385
-61
lines changed

routers/api/v1/api.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import (
8181
"code.gitea.io/gitea/modules/log"
8282
"code.gitea.io/gitea/modules/setting"
8383
api "code.gitea.io/gitea/modules/structs"
84+
"code.gitea.io/gitea/modules/util"
8485
"code.gitea.io/gitea/modules/web"
8586
"code.gitea.io/gitea/routers/api/v1/activitypub"
8687
"code.gitea.io/gitea/routers/api/v1/admin"
@@ -774,7 +775,9 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) {
774775
return func(ctx *context.APIContext) {
775776
ar, err := common.AuthShared(ctx.Base, nil, authMethod)
776777
if err != nil {
777-
ctx.APIError(http.StatusUnauthorized, err)
778+
msg, ok := auth.ErrAsUserAuthMessage(err)
779+
msg = util.Iif(ok, msg, "invalid username, password or token")
780+
ctx.APIError(http.StatusUnauthorized, msg)
778781
return
779782
}
780783
ctx.Doer = ar.Doer

routers/api/v1/repo/issue_dependency.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func CreateIssueDependency(ctx *context.APIContext) {
201201
return
202202
}
203203

204-
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
204+
dependencyPerm := getPermissionForRepo(ctx, dependency.Repo)
205205
if ctx.Written() {
206206
return
207207
}
@@ -262,7 +262,7 @@ func RemoveIssueDependency(ctx *context.APIContext) {
262262
return
263263
}
264264

265-
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
265+
dependencyPerm := getPermissionForRepo(ctx, dependency.Repo)
266266
if ctx.Written() {
267267
return
268268
}

routers/api/v1/repo/release_tags.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88

99
repo_model "code.gitea.io/gitea/models/repo"
10+
unit_model "code.gitea.io/gitea/models/unit"
1011
"code.gitea.io/gitea/services/context"
1112
"code.gitea.io/gitea/services/convert"
1213
release_service "code.gitea.io/gitea/services/release"
@@ -58,6 +59,13 @@ func GetReleaseByTag(ctx *context.APIContext) {
5859
return
5960
}
6061

62+
if release.IsDraft { // only the users with write access can see draft releases
63+
if !ctx.IsSigned || !ctx.Repo.CanWrite(unit_model.TypeReleases) {
64+
ctx.APIErrorNotFound()
65+
return
66+
}
67+
}
68+
6169
if err = release.LoadAttributes(ctx); err != nil {
6270
ctx.APIErrorInternal(err)
6371
return

routers/web/repo/githttp.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,13 @@ func httpBase(ctx *context.Context) *serviceHandler {
146146
// rely on the results of Contexter
147147
if !ctx.IsSigned {
148148
// TODO: support digit auth - which would be Authorization header with digit
149-
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
149+
if setting.OAuth2.Enabled {
150+
// `Basic realm="Gitea"` tells the GCM to use builtin OAuth2 application: https://github.com/git-ecosystem/git-credential-manager/pull/1442
151+
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
152+
} else {
153+
// If OAuth2 is disabled, then use another realm to avoid GCM OAuth2 attempt
154+
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea (Basic Auth)"`)
155+
}
150156
ctx.HTTPError(http.StatusUnauthorized)
151157
return nil
152158
}

routers/web/repo/issue_content_history.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,11 @@ func SoftDeleteContentHistory(ctx *context.Context) {
206206
ctx.NotFound(issues_model.ErrCommentNotExist{})
207207
return
208208
}
209+
if history.CommentID != commentID {
210+
ctx.NotFound(issues_model.ErrCommentNotExist{})
211+
return
212+
}
209213
if commentID != 0 {
210-
if history.CommentID != commentID {
211-
ctx.NotFound(issues_model.ErrCommentNotExist{})
212-
return
213-
}
214-
215214
if comment, err = issues_model.GetCommentByID(ctx, commentID); err != nil {
216215
log.Error("can not get comment for issue content history %v. err=%v", historyID, err)
217216
return

services/auth/auth.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package auth
66

77
import (
8+
"errors"
89
"fmt"
910
"net/http"
1011
"regexp"
@@ -40,6 +41,20 @@ var globalVars = sync.OnceValue(func() *globalVarsStruct {
4041
}
4142
})
4243

44+
type ErrUserAuthMessage string
45+
46+
func (e ErrUserAuthMessage) Error() string {
47+
return string(e)
48+
}
49+
50+
func ErrAsUserAuthMessage(err error) (string, bool) {
51+
var msg ErrUserAuthMessage
52+
if errors.As(err, &msg) {
53+
return msg.Error(), true
54+
}
55+
return "", false
56+
}
57+
4358
// Init should be called exactly once when the application starts to allow plugins
4459
// to allocate necessary resources
4560
func Init() {

services/auth/basic.go

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

77
import (
8-
"errors"
98
"net/http"
109

1110
actions_model "code.gitea.io/gitea/models/actions"
@@ -146,7 +145,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
146145
return nil, err
147146
}
148147
if hasWebAuthn {
149-
return nil, errors.New("basic authorization is not allowed while WebAuthn enrolled")
148+
return nil, ErrUserAuthMessage("basic authorization is not allowed while WebAuthn enrolled")
150149
}
151150

152151
if err := validateTOTP(req, u); err != nil {

services/convert/convert.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,9 @@ func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerifi
542542
}
543543
if verif.SigningUser != nil {
544544
commitVerification.Signer = &api.PayloadUser{
545-
Name: verif.SigningUser.Name,
546-
Email: verif.SigningUser.Email,
545+
UserName: verif.SigningUser.Name,
546+
Name: verif.SigningUser.DisplayName(),
547+
Email: verif.SigningEmail, // Use the email from the signature, not from the user profile
547548
}
548549
}
549550
return commitVerification

services/pull/merge.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,11 +547,15 @@ var escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
547547

548548
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
549549
func IsUserAllowedToMerge(ctx context.Context, pr *issues_model.PullRequest, p access_model.Permission, user *user_model.User) (bool, error) {
550+
return isUserAllowedToMergeInRepoBranch(ctx, pr.BaseRepoID, pr.BaseBranch, p, user)
551+
}
552+
553+
func isUserAllowedToMergeInRepoBranch(ctx context.Context, repoID int64, branch string, p access_model.Permission, user *user_model.User) (bool, error) {
550554
if user == nil {
551555
return false, nil
552556
}
553557

554-
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
558+
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repoID, branch)
555559
if err != nil {
556560
return false, err
557561
}

services/pull/update.go

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
101101
}
102102

103103
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
104+
// update PR means send new commits to PR head branch from base branch
104105
func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (mergeAllowed, rebaseAllowed bool, err error) {
105106
if pull.Flow == issues_model.PullRequestFlowAGit {
106107
return false, false, nil
107108
}
108-
109109
if user == nil {
110110
return false, false, nil
111111
}
@@ -121,61 +121,56 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
121121
return false, false, err
122122
}
123123

124-
pr := &issues_model.PullRequest{
125-
HeadRepoID: pull.BaseRepoID,
126-
HeadRepo: pull.BaseRepo,
127-
BaseRepoID: pull.HeadRepoID,
128-
BaseRepo: pull.HeadRepo,
129-
HeadBranch: pull.BaseBranch,
130-
BaseBranch: pull.HeadBranch,
131-
}
132-
133-
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
134-
if err != nil {
135-
return false, false, err
136-
}
137-
138-
if err := pr.LoadBaseRepo(ctx); err != nil {
139-
return false, false, err
140-
}
141-
prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
142-
if err != nil {
124+
// 1. check base repository's AllowRebaseUpdate configuration
125+
// it is a config in base repo but controls the head (fork) repo's "Update" behavior
126+
{
127+
prBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
143128
if repo_model.IsErrUnitTypeNotExist(err) {
144-
return false, false, nil
129+
return false, false, nil // the PR unit is disabled in base repo
130+
} else if err != nil {
131+
return false, false, fmt.Errorf("get base repo unit: %v", err)
145132
}
146-
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
147-
return false, false, err
133+
rebaseAllowed = prBaseUnit.PullRequestsConfig().AllowRebaseUpdate
148134
}
149135

150-
rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
151-
152-
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
153-
if pb != nil {
154-
pb.Repo = pull.BaseRepo
155-
if !pb.CanUserForcePush(ctx, user) {
156-
rebaseAllowed = false
136+
// 2. check head branch protection whether rebase is allowed, if pb not found then rebase depends on the above setting
137+
{
138+
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.HeadRepoID, pull.HeadBranch)
139+
if err != nil {
140+
return false, false, err
141+
}
142+
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
143+
if pb != nil {
144+
pb.Repo = pull.HeadRepo
145+
rebaseAllowed = rebaseAllowed && pb.CanUserForcePush(ctx, user)
157146
}
158147
}
159148

149+
// 3. check whether user has write access to head branch
160150
baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.BaseRepo, user)
161151
if err != nil {
162152
return false, false, err
163153
}
164154

165-
mergeAllowed, err = IsUserAllowedToMerge(ctx, pr, headRepoPerm, user)
155+
mergeAllowed, err = isUserAllowedToMergeInRepoBranch(ctx, pull.HeadRepoID, pull.HeadBranch, headRepoPerm, user)
166156
if err != nil {
167157
return false, false, err
168158
}
169159

160+
// 4. if the pull creator allows maintainer to edit, it means the write permissions of the head branch has been
161+
// granted to the user with write permission of the base repository
170162
if pull.AllowMaintainerEdit {
171-
mergeAllowedMaintainer, err := IsUserAllowedToMerge(ctx, pr, baseRepoPerm, user)
163+
mergeAllowedMaintainer, err := isUserAllowedToMergeInRepoBranch(ctx, pull.BaseRepoID, pull.BaseBranch, baseRepoPerm, user)
172164
if err != nil {
173165
return false, false, err
174166
}
175167

176168
mergeAllowed = mergeAllowed || mergeAllowedMaintainer
177169
}
178170

171+
// if merge is not allowed, rebase is also not allowed
172+
rebaseAllowed = rebaseAllowed && mergeAllowed
173+
179174
return mergeAllowed, rebaseAllowed, nil
180175
}
181176

0 commit comments

Comments
 (0)