diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 5a5acc3c69..2e5babdf31 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1150,10 +1150,10 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // user should have permission to read baseRepo's codes and pulls, NOT headRepo's - permBase, err := access_model.GetUserRepoPermission(ctx, baseRepo, ctx.Doer) + permBase, err := access_model.GetUserRepoPermissionWithReducer(ctx, baseRepo, ctx.Doer, ctx.Reducer) if err != nil { headGitRepo.Close() - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) + ctx.Error(http.StatusInternalServerError, "GetUserRepoPermissionWithReducer", err) return nil, nil, nil, "", "" } if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(unit.TypeCode) { @@ -1169,10 +1169,10 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // user should have permission to read headrepo's codes - permHead, err := access_model.GetUserRepoPermission(ctx, headRepo, ctx.Doer) + permHead, err := access_model.GetUserRepoPermissionWithReducer(ctx, headRepo, ctx.Doer, ctx.Reducer) if err != nil { headGitRepo.Close() - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) + ctx.Error(http.StatusInternalServerError, "GetUserRepoPermissionWithReducer", err) return nil, nil, nil, "", "" } if !permHead.CanRead(unit.TypeCode) { diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index 42f997eb7d..12941d9ad2 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -18,6 +18,7 @@ import ( user_model "forgejo.org/models/user" "forgejo.org/modules/git" api "forgejo.org/modules/structs" + "forgejo.org/tests" "github.com/stretchr/testify/assert" ) @@ -269,3 +270,49 @@ func testAPICompareCommits(t *testing.T, objectFormat git.ObjectFormat) { } }) } + +func TestAPICompareCommitsAccessTokenResources(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + + // Using the compare API, will be testing that the base repo's security checks implement fine-grained access + // controls (and baselines with all and public-only). + testCase := func(t *testing.T, repo, token string, expectedStatus int) { + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/compare/master...master", repo)).AddTokenAuth(token) + MakeRequest(t, req, expectedStatus) + } + + t.Run("all access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + allToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + + testCase(t, "user2/repo1", allToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", allToken, http.StatusOK) // private org3/repo3 + testCase(t, "user2/repo20", allToken, http.StatusOK) // private user2/repo20 + }) + + t.Run("public-only access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopePublicOnly, auth_model.AccessTokenScopeReadRepository) + + testCase(t, "user2/repo1", publicOnlyToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", publicOnlyToken, http.StatusNotFound) // private org3/repo3 + testCase(t, "user2/repo20", publicOnlyToken, http.StatusNotFound) // private user2/repo20 + }) + + t.Run("specific repo access token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + repo2OnlyToken := createFineGrainedRepoAccessToken(t, "user2", + []auth_model.AccessTokenScope{auth_model.AccessTokenScopeReadRepository}, + []int64{3}, + ) + + testCase(t, "user2/repo1", repo2OnlyToken, http.StatusOK) // public user2/repo1 + testCase(t, "org3/repo3", repo2OnlyToken, http.StatusOK) // private org3/repo3 + testCase(t, "user2/repo20", repo2OnlyToken, http.StatusNotFound) // private user2/repo20, outside of fine-grain + }) +}