Skip to content

Commit 11f774a

Browse files
lunnywxiaoguang
andcommitted
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 5e7207d commit 11f774a

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
@@ -82,6 +82,7 @@ import (
8282
"code.gitea.io/gitea/modules/log"
8383
"code.gitea.io/gitea/modules/setting"
8484
api "code.gitea.io/gitea/modules/structs"
85+
"code.gitea.io/gitea/modules/util"
8586
"code.gitea.io/gitea/modules/web"
8687
"code.gitea.io/gitea/routers/api/v1/activitypub"
8788
"code.gitea.io/gitea/routers/api/v1/admin"
@@ -791,7 +792,9 @@ func apiAuth(authMethod auth.Method) func(*context.APIContext) {
791792
return func(ctx *context.APIContext) {
792793
ar, err := common.AuthShared(ctx.Base, nil, authMethod)
793794
if err != nil {
794-
ctx.APIError(http.StatusUnauthorized, err)
795+
msg, ok := auth.ErrAsUserAuthMessage(err)
796+
msg = util.Iif(ok, msg, "invalid username, password or token")
797+
ctx.APIError(http.StatusUnauthorized, msg)
795798
return
796799
}
797800
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
@@ -147,7 +147,13 @@ func httpBase(ctx *context.Context) *serviceHandler {
147147
// rely on the results of Contexter
148148
if !ctx.IsSigned {
149149
// TODO: support digit auth - which would be Authorization header with digit
150-
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
150+
if setting.OAuth2.Enabled {
151+
// `Basic realm="Gitea"` tells the GCM to use builtin OAuth2 application: https://github.com/git-ecosystem/git-credential-manager/pull/1442
152+
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
153+
} else {
154+
// If OAuth2 is disabled, then use another realm to avoid GCM OAuth2 attempt
155+
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea (Basic Auth)"`)
156+
}
151157
ctx.HTTPError(http.StatusUnauthorized)
152158
return nil
153159
}

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
@@ -546,11 +546,15 @@ var escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
546546

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

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

services/pull/update.go

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

9999
// IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
100+
// update PR means send new commits to PR head branch from base branch
100101
func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, user *user_model.User) (mergeAllowed, rebaseAllowed bool, err error) {
101102
if pull.Flow == issues_model.PullRequestFlowAGit {
102103
return false, false, nil
103104
}
104-
105105
if user == nil {
106106
return false, false, nil
107107
}
@@ -117,61 +117,56 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
117117
return false, false, err
118118
}
119119

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

146-
rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
147-
148-
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
149-
if pb != nil {
150-
pb.Repo = pull.BaseRepo
151-
if !pb.CanUserForcePush(ctx, user) {
152-
rebaseAllowed = false
132+
// 2. check head branch protection whether rebase is allowed, if pb not found then rebase depends on the above setting
133+
{
134+
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pull.HeadRepoID, pull.HeadBranch)
135+
if err != nil {
136+
return false, false, err
137+
}
138+
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
139+
if pb != nil {
140+
pb.Repo = pull.HeadRepo
141+
rebaseAllowed = rebaseAllowed && pb.CanUserForcePush(ctx, user)
153142
}
154143
}
155144

145+
// 3. check whether user has write access to head branch
156146
baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.BaseRepo, user)
157147
if err != nil {
158148
return false, false, err
159149
}
160150

161-
mergeAllowed, err = IsUserAllowedToMerge(ctx, pr, headRepoPerm, user)
151+
mergeAllowed, err = isUserAllowedToMergeInRepoBranch(ctx, pull.HeadRepoID, pull.HeadBranch, headRepoPerm, user)
162152
if err != nil {
163153
return false, false, err
164154
}
165155

156+
// 4. if the pull creator allows maintainer to edit, it means the write permissions of the head branch has been
157+
// granted to the user with write permission of the base repository
166158
if pull.AllowMaintainerEdit {
167-
mergeAllowedMaintainer, err := IsUserAllowedToMerge(ctx, pr, baseRepoPerm, user)
159+
mergeAllowedMaintainer, err := isUserAllowedToMergeInRepoBranch(ctx, pull.BaseRepoID, pull.BaseBranch, baseRepoPerm, user)
168160
if err != nil {
169161
return false, false, err
170162
}
171163

172164
mergeAllowed = mergeAllowed || mergeAllowedMaintainer
173165
}
174166

167+
// if merge is not allowed, rebase is also not allowed
168+
rebaseAllowed = rebaseAllowed && mergeAllowed
169+
175170
return mergeAllowed, rebaseAllowed, nil
176171
}
177172

0 commit comments

Comments
 (0)