mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-26 06:16:56 +09:00
refactor: carry the API token on the doer (#39690)
The token a request authenticated with now lives on the doer, so permission and visibility checks apply its scope from the doer alone. Replaces https://github.com/go-gitea/gitea/pull/39167, following https://github.com/go-gitea/gitea/pull/39167#issuecomment-6063839619. 1. Public-only tokens get 404 instead of 403 for hidden users, orgs and repos, like GitHub. 2. Org ownership, site admin, repo and org creation, generation and transfer endpoints reject public-only tokens. 3. API pull request responses set `head.repo` to `null` and `head.repo_id` to `-1` when the caller cannot see the head repository. 4. Public-only tokens see org memberships like anonymous callers, so concealed members stay hidden. 5. Site admin access in activity feeds, follower lists, org members, Actions run lists and packages needs a session or an `all`-scoped token. Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
1 parent
7e62dda791
commit
5264304a34
96 files changed
+583
-686
No files matched your search
@@ -88,7 +88,7 @@ func (n *actionsNotifier) notifyIssueChangeWithTitleOrContent(ctx context.Contex
|
||||
WithPayload(&api.PullRequestPayload{
|
||||
Action: api.HookIssueEdited,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, nil),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
@@ -120,7 +120,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
|
||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, nil),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
CommitID: commitID,
|
||||
@@ -240,7 +240,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues
|
||||
payload := &api.PullRequestPayload{
|
||||
Action: action,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, nil),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
Changes: changes,
|
||||
@@ -374,7 +374,7 @@ func (n *actionsNotifier) NewPullRequest(ctx context.Context, pull *issues_model
|
||||
WithPayload(&api.PullRequestPayload{
|
||||
Action: api.HookIssueOpened,
|
||||
Index: pull.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pull, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pull, nil),
|
||||
Repository: convert.ToRepo(ctx, pull.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, pull.Issue.Poster, nil),
|
||||
}).
|
||||
@@ -454,7 +454,7 @@ func (n *actionsNotifier) PullRequestReview(ctx context.Context, pr *issues_mode
|
||||
WithPayload(&api.PullRequestPayload{
|
||||
Action: api.HookIssueReviewed,
|
||||
Index: review.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, nil),
|
||||
Repository: convert.ToRepo(ctx, review.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, review.Reviewer, nil),
|
||||
Review: &api.ReviewPayload{
|
||||
@@ -488,7 +488,7 @@ func (n *actionsNotifier) PullRequestReviewRequest(ctx context.Context, doer *us
|
||||
WithPayload(&api.PullRequestPayload{
|
||||
Action: action,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, nil),
|
||||
RequestedReviewer: convert.ToUser(ctx, reviewer, nil),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
@@ -525,7 +525,7 @@ func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.U
|
||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, nil),
|
||||
Repository: convert.ToRepo(ctx, pr.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
Action: api.HookIssueClosed,
|
||||
@@ -695,7 +695,7 @@ func (n *actionsNotifier) PullRequestSynchronized(ctx context.Context, doer *use
|
||||
Before: before,
|
||||
After: after,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, nil),
|
||||
Repository: convert.ToRepo(ctx, pr.Issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
@@ -726,7 +726,7 @@ func (n *actionsNotifier) PullRequestChangeTargetBranch(ctx context.Context, doe
|
||||
From: oldBranch,
|
||||
},
|
||||
},
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, nil),
|
||||
Repository: convert.ToRepo(ctx, pr.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
|
||||
@@ -50,23 +50,6 @@ func doerFromContext(ctx context.Context) *user_model.User {
|
||||
return nil
|
||||
}
|
||||
|
||||
// credentialFromContext returns the credential the surrounding request
|
||||
// authenticated with. It is dropped when the event is recorded for someone
|
||||
// other than the signed-in user, so an explicit actor is never tied to a
|
||||
// credential that is not theirs.
|
||||
func credentialFromContext(ctx context.Context, doer *user_model.User) string {
|
||||
data := middleware.GetContextData(ctx)
|
||||
if data == nil {
|
||||
return ""
|
||||
}
|
||||
signedUser, _ := data[middleware.ContextDataKeySignedUser].(*user_model.User)
|
||||
if signedUser == nil || signedUser.ID != doer.ID {
|
||||
return ""
|
||||
}
|
||||
credential, _ := data[middleware.ContextDataKeyAuthCredential].(string)
|
||||
return credential
|
||||
}
|
||||
|
||||
// ImpersonatorFromContext resolves the admin acting as the doer, so an event
|
||||
// recorded during an impersonated session cannot be pinned on the impersonated
|
||||
// user alone. Returns nil for ordinary sessions.
|
||||
|
||||
@@ -106,25 +106,15 @@ func TestBuildEvent(t *testing.T) {
|
||||
e := buildEvent(context.Background(), RecordParams{
|
||||
Action: audit_model.UserCreate,
|
||||
Actor: actorRef(actions),
|
||||
ActorCredential: actorCredential(context.Background(), actions),
|
||||
ActorCredential: user_model.GetDoerCredential(actions),
|
||||
Scope: ScopeFromUser(u),
|
||||
})
|
||||
assert.Equal(t, user_model.ActionsUserID, e.ActorID)
|
||||
assert.Equal(t, "gitea-actions:42", e.ActorCredential)
|
||||
|
||||
key := user_model.NewDeployKeyUserWithKeyID(7)
|
||||
assert.Equal(t, "deploy-key:7", actorCredential(context.Background(), key))
|
||||
assert.Empty(t, actorCredential(context.Background(), doer))
|
||||
})
|
||||
|
||||
t.Run("CredentialFromRequest", func(t *testing.T) {
|
||||
ctx := newRequestContext(t, doer)
|
||||
middleware.GetContextData(ctx)[middleware.ContextDataKeyAuthCredential] = "access-token:9"
|
||||
assert.Equal(t, "access-token:9", actorCredential(ctx, doer))
|
||||
|
||||
// an event recorded for someone other than the signed-in user is not
|
||||
// tied to the credential of that request
|
||||
assert.Empty(t, actorCredential(ctx, u))
|
||||
assert.Equal(t, "deploy-key:7", user_model.GetDoerCredential(key))
|
||||
assert.Empty(t, user_model.GetDoerCredential(doer))
|
||||
})
|
||||
|
||||
t.Run("RequestInfo", func(t *testing.T) {
|
||||
|
||||
@@ -120,7 +120,7 @@ func RecordAs(ctx context.Context, doer *user_model.User, action audit_model.Act
|
||||
writeEvent(ctx, RecordParams{
|
||||
Action: action,
|
||||
Actor: actorRef(doer),
|
||||
ActorCredential: actorCredential(ctx, doer),
|
||||
ActorCredential: user_model.GetDoerCredential(doer),
|
||||
Impersonator: impersonatorRef(ImpersonatorFromContext(ctx), doer),
|
||||
Scope: scopeRef(scope),
|
||||
Metadata: metaPairs(metadata...),
|
||||
|
||||
@@ -25,20 +25,6 @@ func actorRef(doer *user_model.User) audit_model.EntityRef {
|
||||
return audit_model.EntityRef{Type: audit_model.ScopeUser, ID: doer.ID, Name: doer.Name}
|
||||
}
|
||||
|
||||
// actorCredential names what the actor acted with: the task or key behind a
|
||||
// system user, otherwise the token the surrounding request authenticated with.
|
||||
// An incident then traces one credential across every event it produced,
|
||||
// instead of stopping at the account that owns it.
|
||||
func actorCredential(ctx context.Context, doer *user_model.User) string {
|
||||
if doer == nil {
|
||||
return ""
|
||||
}
|
||||
if doer.ExtDoerData != nil {
|
||||
return doer.ExtDoerData.EncodeToString()
|
||||
}
|
||||
return credentialFromContext(ctx, doer)
|
||||
}
|
||||
|
||||
// impersonatorRef names the admin behind an impersonated session. It is dropped
|
||||
// when the actor is the admin themselves, so events an admin performs before
|
||||
// entering or after leaving an impersonation are not marked as impersonated.
|
||||
|
||||
@@ -115,7 +115,7 @@ func RecordScoped(ctx context.Context, owner *user_model.User, repo *repository_
|
||||
writeEvent(ctx, RecordParams{
|
||||
Action: action,
|
||||
Actor: actorRef(doer),
|
||||
ActorCredential: actorCredential(ctx, doer),
|
||||
ActorCredential: user_model.GetDoerCredential(doer),
|
||||
Scope: scope,
|
||||
Metadata: metaPairs(metadata...),
|
||||
})
|
||||
|
||||
+6
-20
@@ -28,9 +28,6 @@ var (
|
||||
// BasicMethodName is the constant name of the basic authentication method
|
||||
const (
|
||||
BasicMethodName = "basic"
|
||||
AccessTokenMethodName = "access_token"
|
||||
OAuth2TokenMethodName = "oauth2_token"
|
||||
ActionTokenMethodName = "action_token"
|
||||
DeployTokenMethodName = "deploy_token"
|
||||
)
|
||||
|
||||
@@ -86,9 +83,7 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
||||
return nil, nil //nolint:nilnil // the auth method is not applicable
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = OAuth2TokenMethodName
|
||||
store.GetData()["ApiTokenScope"] = accessTokenScope
|
||||
setAuthCredential(store, credentialOAuth2Grant, grantID)
|
||||
u.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialOAuth2Grant, grantID, accessTokenScope)
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -107,9 +102,7 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
||||
log.Error("UpdateAccessToken: %v", err)
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = AccessTokenMethodName
|
||||
store.GetData()["ApiTokenScope"] = token.Scope
|
||||
setAuthCredential(store, credentialAccessToken, token.ID)
|
||||
u.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialAccessToken, token.ID, token.Scope)
|
||||
return u, nil
|
||||
} else if !errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("GetAccessTokenBySHA: %v", err)
|
||||
@@ -119,7 +112,6 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
||||
task, err := actions_model.GetRunningTaskByToken(req.Context(), authToken)
|
||||
if err == nil && task != nil {
|
||||
log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
|
||||
store.GetData()["LoginMethod"] = ActionTokenMethodName
|
||||
return user_model.NewActionsUserWithTaskID(task.ID), nil
|
||||
}
|
||||
return nil, nil //nolint:nilnil // the auth method is not applicable
|
||||
@@ -194,18 +186,12 @@ func validateTOTP(req *http.Request, u *user_model.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAccessScope(store DataStore) auth_model.AccessTokenScope {
|
||||
if scope, hasApiTokenScope := store.GetData()["ApiTokenScope"].(auth_model.AccessTokenScope); hasApiTokenScope {
|
||||
func GetAccessScope(doer *user_model.User, store DataStore) auth_model.AccessTokenScope {
|
||||
if scope, ok := user_model.GetDoerTokenScope(doer); ok {
|
||||
return scope
|
||||
}
|
||||
switch store.GetData()["LoginMethod"] {
|
||||
case OAuth2TokenMethodName:
|
||||
fallthrough
|
||||
case BasicMethodName, AccessTokenMethodName:
|
||||
if store.GetData()["LoginMethod"] == BasicMethodName {
|
||||
return auth_model.AccessTokenScopeAll
|
||||
case ActionTokenMethodName:
|
||||
fallthrough
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"gitea.dev/modules/web/middleware"
|
||||
)
|
||||
|
||||
// Credential kinds naming how a request authenticated, recorded so an audit
|
||||
// event can point at the token rather than only at its owner.
|
||||
const (
|
||||
credentialAccessToken = "access-token"
|
||||
credentialOAuth2Grant = "oauth2-grant"
|
||||
)
|
||||
|
||||
func setAuthCredential(store DataStore, kind string, id int64) {
|
||||
store.GetData()[middleware.ContextDataKeyAuthCredential] = kind + ":" + strconv.FormatInt(id, 10)
|
||||
}
|
||||
@@ -42,6 +42,5 @@ func (d *DeployToken) Verify(req *http.Request, _ http.ResponseWriter, store Dat
|
||||
log.Error("UpdateDeployKeyUpdated: %v", err)
|
||||
}
|
||||
|
||||
store.GetData()["LoginMethod"] = DeployTokenMethodName
|
||||
return user_model.NewDeployKeyUserWithKeyID(key.ID), nil
|
||||
}
|
||||
+10
-9
@@ -107,8 +107,7 @@ func parseToken(req *http.Request) (string, bool) {
|
||||
}
|
||||
|
||||
// userFromToken returns the user corresponding to the OAuth token.
|
||||
// It will set 'ApiTokenScope' to the scope of the access token (TODO: this behavior should be fixed, don't set ctx.Data)
|
||||
func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string, store DataStore) (*user_model.User, error) {
|
||||
func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string) (*user_model.User, error) {
|
||||
// Let's see if token is valid.
|
||||
if strings.Contains(tokenSHA, ".") {
|
||||
// First attempt to decode an actions JWT, returning the actions user
|
||||
@@ -124,8 +123,7 @@ func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string, store DataS
|
||||
if err != nil || !user.IsIndividual() {
|
||||
return nil, err
|
||||
}
|
||||
store.GetData()["ApiTokenScope"] = accessTokenScope
|
||||
setAuthCredential(store, credentialOAuth2Grant, grantID)
|
||||
user.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialOAuth2Grant, grantID, accessTokenScope)
|
||||
return user, nil
|
||||
}
|
||||
t, err := auth_model.GetAccessTokenBySHA(ctx, tokenSHA)
|
||||
@@ -144,22 +142,25 @@ func (o *OAuth2) userFromToken(ctx context.Context, tokenSHA string, store DataS
|
||||
if err = auth_model.UpdateAccessToken(ctx, t); err != nil {
|
||||
log.Error("UpdateAccessToken: %v", err)
|
||||
}
|
||||
store.GetData()["ApiTokenScope"] = t.Scope
|
||||
setAuthCredential(store, credentialAccessToken, t.ID)
|
||||
return user_model.GetUserByID(ctx, t.UID)
|
||||
user, err := user_model.GetUserByID(ctx, t.UID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialAccessToken, t.ID, t.Scope)
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Verify extracts the user ID from the OAuth token in the query parameters
|
||||
// or the "Authorization" header and returns the corresponding user object for that ID.
|
||||
// If verification is successful returns an existing user object.
|
||||
// Returns nil if verification fails.
|
||||
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, _ DataStore, sess SessionStore) (*user_model.User, error) {
|
||||
token, ok := parseToken(req)
|
||||
if !ok {
|
||||
return nil, nil //nolint:nilnil // the auth method is not applicable
|
||||
}
|
||||
|
||||
user, err := o.userFromToken(req.Context(), token, store)
|
||||
user, err := o.userFromToken(req.Context(), token)
|
||||
if err != nil && !errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("userFromToken: %v", err) // the callers might ignore the error, so log it here
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/services/actions"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -23,16 +24,25 @@ func TestUserIDFromToken(t *testing.T) {
|
||||
token, err := actions.CreateAuthorizationToken(RunningTaskID, 1, 2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ds := make(reqctx.ContextData)
|
||||
|
||||
o := OAuth2{}
|
||||
u, err := o.userFromToken(t.Context(), token, ds)
|
||||
u, err := o.userFromToken(t.Context(), token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, user_model.ActionsUserID, u.ID)
|
||||
taskID, ok := user_model.GetActionsUserTaskID(u)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, RunningTaskID, taskID)
|
||||
})
|
||||
|
||||
t.Run("Access token", func(t *testing.T) {
|
||||
token := &auth_model.AccessToken{UID: 2, Name: "public-only", Scope: "public-only,read:repository"}
|
||||
require.NoError(t, auth_model.NewAccessToken(t.Context(), token))
|
||||
|
||||
u, err := (&OAuth2{}).userFromToken(t.Context(), token.Token)
|
||||
require.NoError(t, err)
|
||||
scope, _ := user_model.GetDoerTokenScope(u)
|
||||
assert.Equal(t, token.Scope, scope)
|
||||
assert.Equal(t, fmt.Sprintf("access-token:%d", token.ID), user_model.GetDoerCredential(u))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckTaskIsRunning(t *testing.T) {
|
||||
|
||||
+3
-12
@@ -13,7 +13,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/cache"
|
||||
@@ -44,17 +43,9 @@ type APIContext struct {
|
||||
|
||||
ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
|
||||
|
||||
Repo *Repository
|
||||
Org *APIOrganization
|
||||
Package *Package
|
||||
PublicOnly bool // Whether the request is for a public endpoint
|
||||
}
|
||||
|
||||
// TokenCanAccessRepo reports whether the current API token is allowed to access the repository.
|
||||
// A public-only token cannot reach a private repo or a repo owned by a non-public (limited or
|
||||
// private) owner; any other token is unrestricted by this check.
|
||||
func (ctx *APIContext) TokenCanAccessRepo(repo *repo_model.Repository) bool {
|
||||
return !ctx.PublicOnly || !publicOnlyTokenDeniedRepo(ctx, repo)
|
||||
Repo *Repository
|
||||
Org *APIOrganization
|
||||
Package *Package
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -37,7 +37,7 @@ func RedirectToUser(ctx *Base, doer *user_model.User, userName string, redirectU
|
||||
}
|
||||
|
||||
// Handle Visibility
|
||||
if user.Visibility != structs.VisibleTypePublic && doer == nil {
|
||||
if user.Visibility != structs.VisibleTypePublic && !user_model.DoerCanViewPrivate(doer) {
|
||||
// We must be signed in to see limited or private organizations
|
||||
ctx.HTTPError(http.StatusNotFound, "user does not exist")
|
||||
return
|
||||
|
||||
@@ -91,7 +91,7 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) {
|
||||
org := ctx.Org.Organization
|
||||
|
||||
// Handle Visibility
|
||||
if org.Visibility != structs.VisibleTypePublic && !ctx.IsSigned {
|
||||
if org.Visibility != structs.VisibleTypePublic && !user_model.DoerCanViewPrivate(ctx.Doer) {
|
||||
// We must be signed in to see limited or private organizations
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
|
||||
@@ -110,6 +110,13 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string)) *Pack
|
||||
}
|
||||
|
||||
func determineAccessMode(ctx *Base, pkgOwner, doer *user_model.User) (perm.AccessMode, error) {
|
||||
if !user_model.DoerTokenAllowsOwner(doer, pkgOwner) {
|
||||
return perm.AccessModeNone, nil
|
||||
}
|
||||
if user_model.DoerIsSiteAdminFullAccess(doer) {
|
||||
return perm.AccessModeOwner, nil
|
||||
}
|
||||
|
||||
if setting.Service.RequireSignInViewStrict && (doer == nil || doer.IsGhost()) {
|
||||
return perm.AccessModeNone, nil
|
||||
}
|
||||
|
||||
@@ -23,4 +23,9 @@ func TestDeterminePackageAccessModeForLimitedOwner(t *testing.T) {
|
||||
accessMode, err = determineAccessMode(&Base{}, owner, &user.User{ID: 3, IsActive: true, IsRestricted: true})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, perm.AccessModeNone, accessMode)
|
||||
|
||||
publicOnlyAdmin := &user.User{ID: 4, IsActive: true, IsAdmin: true, ExtDoerData: user.NewTokenExtDoerData(user.CredentialAccessToken, 1, "public-only,read:package")}
|
||||
accessMode, err = determineAccessMode(&Base{}, owner, publicOnlyAdmin)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, perm.AccessModeNone, accessMode)
|
||||
}
|
||||
@@ -4,63 +4,21 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
)
|
||||
|
||||
// isOwnerHidden reports whether repo's owner is not publicly visible (a limited or private owner), so
|
||||
// the owner's repositories must be hidden from callers that may only reach genuinely public resources.
|
||||
func isOwnerHidden(ctx context.Context, repo *repo_model.Repository) bool {
|
||||
if err := repo.LoadOwner(ctx); err != nil || repo.Owner == nil {
|
||||
return true // fail closed if the owner visibility can't be determined
|
||||
}
|
||||
return !repo.Owner.Visibility.IsPublic()
|
||||
}
|
||||
|
||||
// publicOnlyTokenDeniedRepo reports whether a public-only API token must be denied access to
|
||||
// repo. A public-only token may only reach genuinely public resources, so it is denied for
|
||||
// private repos and for repos owned by a non-public (limited or private) owner.
|
||||
func publicOnlyTokenDeniedRepo(ctx context.Context, repo *repo_model.Repository) bool {
|
||||
if repo == nil {
|
||||
return false
|
||||
}
|
||||
return repo.IsPrivate || isOwnerHidden(ctx, repo)
|
||||
}
|
||||
|
||||
// TokenIsPublicOnly reports whether the request is authenticated by a public-only API token. A
|
||||
// non-token request, or a token with no recorded scope, is not public-only.
|
||||
func TokenIsPublicOnly(ctx *Context) bool {
|
||||
scope, hasApiTokenScope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
|
||||
if !hasApiTokenScope {
|
||||
return false
|
||||
}
|
||||
publicOnly, _ := scope.PublicOnly()
|
||||
return publicOnly
|
||||
}
|
||||
|
||||
// CheckTokenScopes checks whether the authenticated API token contains any of the given scopes.
|
||||
func CheckTokenScopes(ctx *Context, repo *repo_model.Repository, scopes ...auth_model.AccessTokenScope) {
|
||||
scope, hasApiTokenScope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
|
||||
func CheckTokenScopes(ctx *Context, scopes ...auth_model.AccessTokenScope) {
|
||||
scope, hasApiTokenScope := user_model.GetDoerTokenScope(ctx.Doer)
|
||||
if !hasApiTokenScope {
|
||||
return
|
||||
}
|
||||
|
||||
publicOnly, err := scope.PublicOnly()
|
||||
if err != nil {
|
||||
ctx.ServerError("PublicOnly", err)
|
||||
return
|
||||
}
|
||||
|
||||
if publicOnly && publicOnlyTokenDeniedRepo(ctx, repo) {
|
||||
ctx.HTTPError(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
scopeMatched, err := scope.HasAnyScope(scopes...)
|
||||
if err != nil {
|
||||
ctx.ServerError("HasAnyScope", err)
|
||||
@@ -118,6 +76,6 @@ func RequireUnitReader(unitTypes ...unit.Type) func(ctx *Context) {
|
||||
}
|
||||
|
||||
// CheckRepoScopedToken checks whether the authenticated API token has repo scope.
|
||||
func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_model.AccessTokenScopeLevel) {
|
||||
CheckTokenScopes(ctx, repo, auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository)...)
|
||||
func CheckRepoScopedToken(ctx *Context, level auth_model.AccessTokenScopeLevel) {
|
||||
CheckTokenScopes(ctx, auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository)...)
|
||||
}
|
||||
@@ -55,8 +55,13 @@ func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string)) (
|
||||
} else {
|
||||
errCb(http.StatusInternalServerError, fmt.Sprintf("GetUserByName: %v", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if !user_model.DoerTokenAllowsOwner(doer, contextUser) {
|
||||
errCb(http.StatusNotFound, user_model.ErrUserRedirectNotExist{Name: username}.Error())
|
||||
return nil
|
||||
}
|
||||
return contextUser
|
||||
}
|
||||
|
||||
|
||||
+40
-23
@@ -22,10 +22,20 @@ import (
|
||||
"gitea.dev/services/gitdiff"
|
||||
)
|
||||
|
||||
// ToAPIPullRequest assumes following fields have been assigned with valid values:
|
||||
// ToAPIPullRequest hides a head repository the doer cannot see
|
||||
func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest {
|
||||
return toAPIPullRequest(ctx, pr, doer, false)
|
||||
}
|
||||
|
||||
// ToPayloadPullRequest keeps the head repository regardless of the doer, for webhook and Actions payloads
|
||||
func ToPayloadPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest {
|
||||
return toAPIPullRequest(ctx, pr, doer, true)
|
||||
}
|
||||
|
||||
// toAPIPullRequest assumes following fields have been assigned with valid values:
|
||||
// Required - Issue
|
||||
// Optional - Merger
|
||||
func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest {
|
||||
func toAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, isPayload bool) *api.PullRequest {
|
||||
var (
|
||||
baseBranch string
|
||||
headBranch string
|
||||
@@ -181,20 +191,25 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
||||
p.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
|
||||
apiPullRequest.Head.RepoID = pr.HeadRepo.ID
|
||||
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
|
||||
// a hidden head repository is read from the base repository's PR ref
|
||||
headGitRepo := gitRepo
|
||||
exist = false
|
||||
if isPayload || pr.HeadRepoID == pr.BaseRepoID || p.HasAnyUnitAccessOrPublicAccess() {
|
||||
apiPullRequest.Head.RepoID = pr.HeadRepo.ID
|
||||
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
|
||||
|
||||
headGitRepo, err := git.OpenRepository(ctx, pr.HeadRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.FullName(), err)
|
||||
return nil
|
||||
}
|
||||
defer headGitRepo.Close()
|
||||
headGitRepo, err = git.OpenRepository(ctx, pr.HeadRepo)
|
||||
if err != nil {
|
||||
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.FullName(), err)
|
||||
return nil
|
||||
}
|
||||
defer headGitRepo.Close()
|
||||
|
||||
exist, err = git_model.IsBranchExist(ctx, pr.HeadRepoID, pr.HeadBranch)
|
||||
if err != nil {
|
||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
|
||||
return nil
|
||||
exist, err = git_model.IsBranchExist(ctx, pr.HeadRepoID, pr.HeadBranch)
|
||||
if err != nil {
|
||||
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Outer scope variables to be used in diff calculation
|
||||
@@ -419,7 +434,17 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
|
||||
}
|
||||
|
||||
// pull request head branch, both repository and branch could not exist
|
||||
if pr.HeadRepo != nil {
|
||||
if pr.HeadRepo != nil && pr.HeadRepoID != pr.BaseRepoID {
|
||||
p, err := access_model.GetDoerRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
p.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
if p.HasAnyUnitAccessOrPublicAccess() {
|
||||
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
|
||||
}
|
||||
}
|
||||
if apiPullRequest.Head.Repository != nil {
|
||||
apiPullRequest.Head.RepoID = pr.HeadRepo.ID
|
||||
exist, err := git_model.IsBranchExist(ctx, pr.HeadRepo.ID, pr.HeadBranch)
|
||||
if err != nil {
|
||||
@@ -429,14 +454,6 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs
|
||||
if exist {
|
||||
apiPullRequest.Head.Ref = pr.HeadBranch
|
||||
}
|
||||
if pr.HeadRepoID != pr.BaseRepoID {
|
||||
p, err := access_model.GetDoerRepoPermission(ctx, pr.HeadRepo, doer)
|
||||
if err != nil {
|
||||
log.Error("GetDoerRepoPermission[%d]: %v", pr.HeadRepoID, err)
|
||||
p.AccessMode = perm.AccessModeNone
|
||||
}
|
||||
apiPullRequest.Head.Repository = ToRepo(ctx, pr.HeadRepo, p)
|
||||
}
|
||||
}
|
||||
if apiPullRequest.Head.Ref == "" {
|
||||
apiPullRequest.Head.Ref = pr.GetGitHeadRefName()
|
||||
|
||||
@@ -59,7 +59,7 @@ func GetListLockHandler(ctx *context.Context) {
|
||||
}
|
||||
repository.MustOwner(ctx)
|
||||
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Read)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func PostLockHandler(ctx *context.Context) {
|
||||
}
|
||||
repository.MustOwner(ctx)
|
||||
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Write)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -213,7 +213,7 @@ func VerifyLockHandler(ctx *context.Context) {
|
||||
}
|
||||
repository.MustOwner(ctx)
|
||||
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Read)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func UnLockHandler(ctx *context.Context) {
|
||||
}
|
||||
repository.MustOwner(ctx)
|
||||
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Write)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -473,9 +473,9 @@ func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requir
|
||||
}
|
||||
|
||||
if requireWrite {
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Write)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Write)
|
||||
} else {
|
||||
context.CheckRepoScopedToken(ctx, repository, auth_model.Read)
|
||||
context.CheckRepoScopedToken(ctx, auth_model.Read)
|
||||
}
|
||||
|
||||
if ctx.Written() {
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ func ChangeOrganizationVisibility(ctx context.Context, org *org_model.Organizati
|
||||
}
|
||||
|
||||
repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
||||
Actor: org.AsUser(), Private: true, ListOptions: db.ListOptionsAll,
|
||||
OwnerID: org.ID, Private: true, ListOptions: db.ListOptionsAll,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -145,6 +145,9 @@ func CheckUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullReques
|
||||
if err := pull.LoadHeadRepo(ctx); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
if !repo_model.DoerTokenAllowsRepo(ctx, user, pull.HeadRepo) { // whitelists and maintainer edits below skip the doer's repo permission
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 1. check whether pull request enabled.
|
||||
prBaseUnit, err := pull.BaseRepo.GetUnit(ctx, unit.TypePullRequests)
|
||||
|
||||
@@ -122,6 +122,32 @@ func TestIsUserAllowedToUpdate(t *testing.T) {
|
||||
|
||||
pr3Poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 12})
|
||||
|
||||
t.Run("PublicOnlyTokenCannotUpdatePrivateHeadEvenWhitelisted", func(t *testing.T) {
|
||||
pr3 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3})
|
||||
protectedBranch := &git_model.ProtectedBranch{
|
||||
RepoID: pr3.HeadRepoID,
|
||||
RuleName: pr3.HeadBranch,
|
||||
CanPush: true,
|
||||
EnableWhitelist: true,
|
||||
WhitelistUserIDs: []int64{pr3Poster.ID},
|
||||
}
|
||||
_, err := db.GetEngine(t.Context()).Insert(protectedBranch)
|
||||
require.NoError(t, err)
|
||||
defer db.DeleteByBean(t.Context(), protectedBranch)
|
||||
require.NoError(t, pr3.LoadHeadRepo(t.Context()))
|
||||
pr3.HeadRepo.IsPrivate = true
|
||||
|
||||
pushAllowed, _, _, err := checkUserAllowedToUpdate(t.Context(), pr3, pr3Poster)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, pushAllowed)
|
||||
|
||||
publicOnlyPoster := *pr3Poster
|
||||
publicOnlyPoster.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialAccessToken, 1, "public-only,write:repository")
|
||||
pushAllowed, _, _, err = checkUserAllowedToUpdate(t.Context(), pr3, &publicOnlyPoster)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, pushAllowed)
|
||||
})
|
||||
|
||||
t.Run("MaintainerEditRespectsPosterPermissions", func(t *testing.T) {
|
||||
pr3 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3})
|
||||
pr3.AllowMaintainerEdit = true
|
||||
|
||||
@@ -265,7 +265,7 @@ func checkUnadoptedRepositories(ctx context.Context, userName string, repoNamesT
|
||||
return err
|
||||
}
|
||||
repos, _, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
||||
Actor: ctxUser,
|
||||
OwnerID: ctxUser.ID,
|
||||
Private: true,
|
||||
ListOptions: db.ListOptions{
|
||||
Page: 1,
|
||||
|
||||
@@ -398,7 +398,6 @@ func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User
|
||||
},
|
||||
Private: true,
|
||||
OwnerID: owner.ID,
|
||||
Actor: owner,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserRepositories: %w", err)
|
||||
|
||||
@@ -65,7 +65,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model
|
||||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequestLabel, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
@@ -179,7 +179,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo
|
||||
Action: action,
|
||||
Changes: changes,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}
|
||||
@@ -222,7 +222,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model
|
||||
From: oldTitle,
|
||||
},
|
||||
},
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
@@ -257,7 +257,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode
|
||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
CommitID: commitID,
|
||||
@@ -320,7 +320,7 @@ func (m *webhookNotifier) DeleteIssue(ctx context.Context, doer *user_model.User
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueDeleted,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}); err != nil {
|
||||
@@ -365,7 +365,7 @@ func (m *webhookNotifier) NewPullRequest(ctx context.Context, pull *issues_model
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: pull.Issue.Repo}, webhook_module.HookEventPullRequest, &api.PullRequestPayload{
|
||||
Action: api.HookIssueOpened,
|
||||
Index: pull.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pull, pull.Issue.Poster),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pull, pull.Issue.Poster),
|
||||
Repository: convert.ToRepo(ctx, pull.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, pull.Issue.Poster, nil),
|
||||
}); err != nil {
|
||||
@@ -394,7 +394,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod
|
||||
From: oldContent,
|
||||
},
|
||||
},
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
@@ -436,7 +436,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us
|
||||
var pullRequest *api.PullRequest
|
||||
if c.Issue.IsPull {
|
||||
eventType = webhook_module.HookEventPullRequestComment
|
||||
pullRequest = convert.ToAPIPullRequest(ctx, c.Issue.PullRequest, doer)
|
||||
pullRequest = convert.ToPayloadPullRequest(ctx, c.Issue.PullRequest, doer)
|
||||
} else {
|
||||
eventType = webhook_module.HookEventIssueComment
|
||||
}
|
||||
@@ -471,7 +471,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod
|
||||
log.Error("LoadPullRequest: %v", err)
|
||||
return
|
||||
}
|
||||
pullRequest = convert.ToAPIPullRequest(ctx, issue.PullRequest, doer)
|
||||
pullRequest = convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer)
|
||||
} else {
|
||||
eventType = webhook_module.HookEventIssueComment
|
||||
}
|
||||
@@ -512,7 +512,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
|
||||
var pullRequest *api.PullRequest
|
||||
if comment.Issue.IsPull {
|
||||
eventType = webhook_module.HookEventPullRequestComment
|
||||
pullRequest = convert.ToAPIPullRequest(ctx, comment.Issue.PullRequest, doer)
|
||||
pullRequest = convert.ToPayloadPullRequest(ctx, comment.Issue.PullRequest, doer)
|
||||
} else {
|
||||
eventType = webhook_module.HookEventIssueComment
|
||||
}
|
||||
@@ -597,7 +597,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode
|
||||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequestLabel, &api.PullRequestPayload{
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
@@ -639,7 +639,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m
|
||||
err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventPullRequestMilestone, &api.PullRequestPayload{
|
||||
Action: hookAction,
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
})
|
||||
@@ -712,7 +712,7 @@ func (*webhookNotifier) MergePullRequest(ctx context.Context, doer *user_model.U
|
||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, doer),
|
||||
Repository: convert.ToRepo(ctx, pr.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
Action: api.HookIssueClosed,
|
||||
@@ -740,7 +740,7 @@ func (m *webhookNotifier) PullRequestChangeTargetBranch(ctx context.Context, doe
|
||||
From: oldBranch,
|
||||
},
|
||||
},
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, doer),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, mode),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}); err != nil {
|
||||
@@ -777,7 +777,7 @@ func (m *webhookNotifier) PullRequestReview(ctx context.Context, pr *issues_mode
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: review.Issue.Repo}, reviewHookType, &api.PullRequestPayload{
|
||||
Action: api.HookIssueReviewed,
|
||||
Index: review.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, review.Reviewer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, review.Reviewer),
|
||||
RequestedReviewer: convert.ToUser(ctx, review.Reviewer, nil),
|
||||
Repository: convert.ToRepo(ctx, review.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, review.Reviewer, nil),
|
||||
@@ -802,7 +802,7 @@ func (m *webhookNotifier) PullRequestReviewRequest(ctx context.Context, doer *us
|
||||
}
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, issue.PullRequest, doer),
|
||||
RequestedReviewer: convert.ToUser(ctx, reviewer, nil),
|
||||
Repository: convert.ToRepo(ctx, issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
@@ -847,7 +847,7 @@ func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *use
|
||||
Before: before,
|
||||
After: after,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: convert.ToAPIPullRequest(ctx, pr, doer),
|
||||
PullRequest: convert.ToPayloadPullRequest(ctx, pr, doer),
|
||||
Repository: convert.ToRepo(ctx, pr.Issue.Repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}); err != nil {
|
||||
|
||||
Reference in new issue
Block a user