mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-02 22:01:44 +02:00
Merge branch 'go-gitea:main' into main
This commit is contained in:
101 files changed
+3947
-1365
No files matched your search
@@ -5,14 +5,23 @@ package integration
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
org_model "code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -20,98 +29,388 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestActionsJobTokenAccess(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
t.Run("Write Access", testActionsJobTokenAccess(u, false))
|
||||
t.Run("Read Access", testActionsJobTokenAccess(u, true))
|
||||
})
|
||||
}
|
||||
func TestActionsJobTokenPermissiveAccess(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
isFork bool
|
||||
|
||||
func testActionsJobTokenAccess(u *url.URL, isFork bool) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47})
|
||||
require.NoError(t, task.GenerateToken())
|
||||
task.Status = actions_model.StatusRunning
|
||||
task.IsForkPullRequest = isFork
|
||||
err := actions_model.UpdateTask(t.Context(), task, "token_hash", "token_salt", "token_last_eight", "status", "is_fork_pull_request")
|
||||
require.NoError(t, err)
|
||||
session := emptyTestSession(t)
|
||||
context := APITestContext{
|
||||
Session: session,
|
||||
Token: task.Token,
|
||||
Username: "user5",
|
||||
Reponame: "repo4",
|
||||
}
|
||||
dstPath := t.TempDir()
|
||||
ownerPermMode repo_model.ActionsTokenPermissionMode
|
||||
ownerMaxPerms map[unit_model.Type]perm.AccessMode
|
||||
|
||||
u.Path = context.GitPath()
|
||||
u.User = url.UserPassword("gitea-actions", task.Token)
|
||||
repoPermMode repo_model.ActionsTokenPermissionMode
|
||||
repoMaxPerms map[unit_model.Type]perm.AccessMode
|
||||
|
||||
t.Run("Git Clone", doGitClone(dstPath, u))
|
||||
expectGitAccess perm.AccessMode
|
||||
}{
|
||||
{
|
||||
name: "OwnerConfig-Permissive",
|
||||
ownerPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
expectGitAccess: perm.AccessModeWrite,
|
||||
},
|
||||
{
|
||||
name: "OwnerConfig-Permissive-CodeNone",
|
||||
ownerPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
ownerMaxPerms: map[unit_model.Type]perm.AccessMode{unit_model.TypeCode: perm.AccessModeNone},
|
||||
expectGitAccess: perm.AccessModeNone,
|
||||
},
|
||||
{
|
||||
name: "OwnerConfig-Restricted",
|
||||
ownerPermMode: repo_model.ActionsTokenPermissionModeRestricted,
|
||||
expectGitAccess: perm.AccessModeRead,
|
||||
},
|
||||
|
||||
t.Run("API Get Repository", doAPIGetRepository(context, func(t *testing.T, r structs.Repository) {
|
||||
require.Equal(t, "repo4", r.Name)
|
||||
require.Equal(t, "user5", r.Owner.UserName)
|
||||
}))
|
||||
// repo uses its own settings, so owner settings should not affect it
|
||||
{
|
||||
name: "SameRepo-Permissive",
|
||||
ownerPermMode: repo_model.ActionsTokenPermissionModeRestricted,
|
||||
ownerMaxPerms: map[unit_model.Type]perm.AccessMode{unit_model.TypeCode: perm.AccessModeNone},
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
expectGitAccess: perm.AccessModeWrite,
|
||||
},
|
||||
{
|
||||
name: "SameRepo-Permissive-CodeNone",
|
||||
ownerPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
ownerMaxPerms: map[unit_model.Type]perm.AccessMode{unit_model.TypeCode: perm.AccessModeRead},
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
repoMaxPerms: map[unit_model.Type]perm.AccessMode{unit_model.TypeCode: perm.AccessModeNone},
|
||||
expectGitAccess: perm.AccessModeNone,
|
||||
},
|
||||
{
|
||||
name: "SameRepo-Restricted",
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModeRestricted,
|
||||
expectGitAccess: perm.AccessModeRead,
|
||||
},
|
||||
|
||||
context.ExpectedCode = util.Iif(isFork, http.StatusForbidden, http.StatusCreated)
|
||||
t.Run("API Create File", doAPICreateFile(context, "test.txt", &structs.CreateFileOptions{
|
||||
FileOptions: structs.FileOptions{
|
||||
NewBranchName: "new-branch",
|
||||
Message: "Create File",
|
||||
},
|
||||
ContentBase64: base64.StdEncoding.EncodeToString([]byte(`This is a test file created using job token.`)),
|
||||
}))
|
||||
|
||||
context.ExpectedCode = http.StatusForbidden
|
||||
t.Run("Fail to Create Repository", doAPICreateRepository(context, true))
|
||||
|
||||
context.ExpectedCode = http.StatusForbidden
|
||||
t.Run("Fail to Delete Repository", doAPIDeleteRepository(context))
|
||||
|
||||
t.Run("Fail to Create Organization", doAPICreateOrganization(context, &structs.CreateOrgOption{
|
||||
UserName: "actions",
|
||||
FullName: "Gitea Actions",
|
||||
}))
|
||||
// forks should be always restricted to max read access for code
|
||||
{
|
||||
name: "Fork-Permissive",
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
isFork: true,
|
||||
expectGitAccess: perm.AccessModeRead,
|
||||
},
|
||||
{
|
||||
name: "Fork-Restricted",
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModeRestricted,
|
||||
isFork: true,
|
||||
expectGitAccess: perm.AccessModeRead,
|
||||
},
|
||||
{
|
||||
name: "Fork-Restricted-CodeNone",
|
||||
repoPermMode: repo_model.ActionsTokenPermissionModeRestricted,
|
||||
repoMaxPerms: map[unit_model.Type]perm.AccessMode{unit_model.TypeCode: perm.AccessModeNone},
|
||||
isFork: true,
|
||||
expectGitAccess: perm.AccessModeNone,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionsJobTokenAccessLFS(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository)
|
||||
t.Run("Create Repository", doAPICreateRepository(httpContext, false, func(t *testing.T, repository structs.Repository) {
|
||||
task := &actions_model.ActionTask{}
|
||||
require.NoError(t, task.GenerateToken())
|
||||
task.Status = actions_model.StatusRunning
|
||||
task.IsForkPullRequest = false
|
||||
task.RepoID = repository.ID
|
||||
err := db.Insert(t.Context(), task)
|
||||
require.NoError(t, err)
|
||||
session := emptyTestSession(t)
|
||||
httpContext := APITestContext{
|
||||
Session: session,
|
||||
Token: task.Token,
|
||||
Username: "user2",
|
||||
Reponame: "repo-lfs-test",
|
||||
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47})
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: task.RepoID})
|
||||
repoActionsUnit := repo.MustGetUnit(t.Context(), unit_model.TypeActions)
|
||||
repoActionsCfg := repoActionsUnit.ActionsConfig()
|
||||
ownerActionsCfg, err := actions_model.GetOwnerActionsConfig(t.Context(), repo.OwnerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.GetEngine(t.Context()).ID(task.RepoID).Cols("is_private").Update(&repo_model.Repository{IsPrivate: true})
|
||||
require.NoError(t, err)
|
||||
|
||||
assertRespCodeForSuccess := func(t *testing.T, resp *httptest.ResponseRecorder, succeed bool) {
|
||||
if succeed {
|
||||
assert.True(t, 200 <= resp.Code && resp.Code < 300, "Expected success status code, got %d", resp.Code)
|
||||
} else {
|
||||
assert.True(t, 400 <= resp.Code && resp.Code < 500, "Expected client error status code, got %d", resp.Code)
|
||||
}
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// prepare owner's token permissions settings
|
||||
ownerActionsCfg.TokenPermissionMode = tt.ownerPermMode
|
||||
ownerActionsCfg.MaxTokenPermissions = util.Iif(tt.ownerMaxPerms == nil, nil, &repo_model.ActionsTokenPermissions{UnitAccessModes: tt.ownerMaxPerms})
|
||||
require.NoError(t, actions_model.SetOwnerActionsConfig(t.Context(), repo.OwnerID, ownerActionsCfg))
|
||||
|
||||
u.Path = httpContext.GitPath()
|
||||
dstPath := t.TempDir()
|
||||
// prepare repo's token permissions settings
|
||||
repoActionsCfg.OverrideOwnerConfig = tt.repoPermMode != "" || tt.repoMaxPerms != nil
|
||||
repoActionsCfg.TokenPermissionMode = tt.repoPermMode
|
||||
repoActionsCfg.MaxTokenPermissions = util.Iif(tt.repoMaxPerms == nil, nil, &repo_model.ActionsTokenPermissions{UnitAccessModes: tt.repoMaxPerms})
|
||||
require.NoError(t, repo_model.UpdateRepoUnitConfig(t.Context(), repoActionsUnit))
|
||||
|
||||
u.Path = httpContext.GitPath()
|
||||
u.User = url.UserPassword("gitea-actions", task.Token)
|
||||
// prepare task and its token
|
||||
require.NoError(t, task.GenerateToken())
|
||||
task.Status = actions_model.StatusRunning
|
||||
task.IsForkPullRequest = tt.isFork
|
||||
err := actions_model.UpdateTask(t.Context(), task, "token_hash", "token_salt", "token_last_eight", "status", "is_fork_pull_request")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Clone", doGitClone(dstPath, u))
|
||||
require.NoError(t, task.LoadJob(t.Context()))
|
||||
require.NoError(t, task.Job.LoadRun(t.Context()))
|
||||
task.Job.Run.IsForkPullRequest = tt.isFork
|
||||
require.NoError(t, actions_model.UpdateRun(t.Context(), task.Job.Run, "is_fork_pull_request"))
|
||||
|
||||
dstPath2 := t.TempDir()
|
||||
testURL := *u
|
||||
testURL.User = url.UserPassword("gitea-actions", task.Token)
|
||||
|
||||
t.Run("Partial Clone", doPartialGitClone(dstPath2, u))
|
||||
t.Run("ReadGitContent", func(t *testing.T) {
|
||||
testURL.Path = "/user5/repo4.git/HEAD"
|
||||
resp := MakeRequest(t, NewRequest(t, "GET", testURL.String()), NoExpectedStatus)
|
||||
assertRespCodeForSuccess(t, resp, tt.expectGitAccess != perm.AccessModeNone)
|
||||
|
||||
lfs := lfsCommitAndPushTest(t, dstPath, testFileSizeSmall)[0]
|
||||
testURL.Path = "/user5/repo4.git/info/lfs/locks"
|
||||
req := NewRequest(t, "GET", testURL.String()).SetHeader("Accept", lfs.MediaType)
|
||||
resp = MakeRequest(t, req, NoExpectedStatus)
|
||||
assertRespCodeForSuccess(t, resp, tt.expectGitAccess != perm.AccessModeNone)
|
||||
})
|
||||
|
||||
reqLFS := NewRequest(t, "GET", "/api/v1/repos/user2/repo-lfs-test/media/"+lfs).AddTokenAuth(task.Token)
|
||||
respLFS := MakeRequestNilResponseRecorder(t, reqLFS, http.StatusOK)
|
||||
assert.Equal(t, testFileSizeSmall, respLFS.Length)
|
||||
}))
|
||||
t.Run("WriteGitContent", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/contents/test-filename", repo.FullName()), &structs.CreateFileOptions{
|
||||
FileOptions: structs.FileOptions{NewBranchName: "new-branch" + t.Name()},
|
||||
ContentBase64: base64.StdEncoding.EncodeToString([]byte(`dummy content`)),
|
||||
}).AddTokenAuth(task.Token)
|
||||
resp := MakeRequest(t, req, NoExpectedStatus)
|
||||
assertRespCodeForSuccess(t, resp, tt.expectGitAccess == perm.AccessModeWrite)
|
||||
|
||||
testURL.Path = "/user5/repo4.git/info/lfs/objects/batch"
|
||||
req = NewRequestWithJSON(t, "POST", testURL.String(), lfs.BatchRequest{Operation: "upload"}).SetHeader("Accept", lfs.MediaType)
|
||||
resp = MakeRequest(t, req, NoExpectedStatus)
|
||||
assertRespCodeForSuccess(t, resp, tt.expectGitAccess == perm.AccessModeWrite)
|
||||
})
|
||||
|
||||
t.Run("NoOtherPermissions", func(t *testing.T) {
|
||||
req := NewRequest(t, "DELETE", "/api/v1/repos/"+repo.FullName()).AddTokenAuth(task.Token)
|
||||
resp := MakeRequest(t, req, NoExpectedStatus)
|
||||
assertRespCodeForSuccess(t, resp, false)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestActionsCrossRepoAccess(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
session := loginUser(t, "user2")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteOrganization)
|
||||
|
||||
// 1. Create Organization
|
||||
orgName := "org-cross-test"
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &structs.CreateOrgOption{
|
||||
UserName: orgName,
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
owner, err := org_model.GetOrgByName(t.Context(), orgName)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 2. Create Two Repositories in owner
|
||||
createRepoInOrg := func(name string) int64 {
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName), &structs.CreateRepoOption{
|
||||
Name: name,
|
||||
AutoInit: true,
|
||||
}).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
var repo structs.Repository
|
||||
DecodeJSON(t, resp, &repo)
|
||||
return repo.ID
|
||||
}
|
||||
|
||||
repoAID := createRepoInOrg("repo-A")
|
||||
repoBID := createRepoInOrg("repo-B")
|
||||
|
||||
// 3. Enable Actions in Repo A (Source) and Repo B (Target)
|
||||
enableActions := func(repoID int64) {
|
||||
err := db.Insert(t.Context(), &repo_model.RepoUnit{
|
||||
RepoID: repoID,
|
||||
Type: unit_model.TypeActions,
|
||||
Config: &repo_model.ActionsConfig{
|
||||
TokenPermissionMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
enableActions(repoAID)
|
||||
enableActions(repoBID)
|
||||
|
||||
// 4. Create Task in Repo A, and use A's token to access B
|
||||
taskA := createActionTask(t, repoAID, false)
|
||||
testCtxA := APITestContext{
|
||||
Session: emptyTestSession(t),
|
||||
Token: taskA.Token,
|
||||
Username: orgName,
|
||||
Reponame: "repo-B",
|
||||
}
|
||||
|
||||
testCtxA.ExpectedCode = http.StatusOK
|
||||
t.Run("PublicCrossRepoAccess", doAPIGetRepository(testCtxA, func(t *testing.T, r structs.Repository) {
|
||||
assert.Equal(t, "repo-B", r.Name)
|
||||
}))
|
||||
|
||||
// make repo-B be private
|
||||
req = NewRequestWithJSON(t, "PATCH", "/api/v1/repos/org-cross-test/repo-B", &structs.EditRepoOption{Private: new(true)}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
testCtxA.ExpectedCode = http.StatusNotFound
|
||||
t.Run("NoPrivateCrossRepoAccess", doAPIGetRepository(testCtxA, nil))
|
||||
|
||||
ownerActionsCfg := actions_model.OwnerActionsConfig{AllowedCrossRepoIDs: []int64{repoBID}}
|
||||
require.NoError(t, actions_model.SetOwnerActionsConfig(t.Context(), owner.ID, ownerActionsCfg))
|
||||
|
||||
testCtxA.ExpectedCode = http.StatusOK
|
||||
t.Run("AccessToSelectedPrivateRepo", doAPIGetRepository(testCtxA, func(t *testing.T, r structs.Repository) {
|
||||
assert.Equal(t, "repo-B", r.Name)
|
||||
}))
|
||||
|
||||
t.Run("RepoTransfer", func(t *testing.T) {
|
||||
ownerActionsCfg, err := actions_model.GetOwnerActionsConfig(t.Context(), owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, ownerActionsCfg.AllowedCrossRepoIDs, repoBID)
|
||||
|
||||
// Transfer Repository to user4
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/repo-B/transfer", orgName), &structs.TransferRepoOption{
|
||||
NewOwner: "user4",
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
// Accept transfer as user4
|
||||
session4 := loginUser(t, "user4")
|
||||
token4 := getTokenForLoggedInUser(t, session4, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository)
|
||||
req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/repo-B/transfer/accept", orgName)).AddTokenAuth(token4)
|
||||
MakeRequest(t, req, http.StatusAccepted)
|
||||
|
||||
// Verify it is removed from the org's config
|
||||
ownerActionsCfg, err = actions_model.GetOwnerActionsConfig(t.Context(), owner.ID)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, ownerActionsCfg.AllowedCrossRepoIDs, repoBID)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func createActionTask(t *testing.T, repoID int64, isFork bool) *actions_model.ActionTask {
|
||||
job := &actions_model.ActionRunJob{
|
||||
RepoID: repoID,
|
||||
Status: actions_model.StatusRunning,
|
||||
IsForkPullRequest: isFork,
|
||||
JobID: "test_job",
|
||||
Name: "test_job",
|
||||
}
|
||||
require.NoError(t, db.Insert(t.Context(), job))
|
||||
task := &actions_model.ActionTask{
|
||||
JobID: job.ID,
|
||||
RepoID: repoID,
|
||||
Status: actions_model.StatusRunning,
|
||||
IsForkPullRequest: isFork,
|
||||
}
|
||||
require.NoError(t, task.GenerateToken())
|
||||
require.NoError(t, db.Insert(t.Context(), task))
|
||||
return task
|
||||
}
|
||||
|
||||
func TestActionsTokenPermissionsPersistenceWithWorkflow(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
session := loginUser(t, user2.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
// create repos
|
||||
repo1 := createActionsTestRepo(t, token, "actions-permission-repo1", false)
|
||||
repo2 := createActionsTestRepo(t, token, "actions-permission-repo2", true)
|
||||
|
||||
// add repo2 to owner-level cross-repo access list
|
||||
req := NewRequestWithValues(t, "POST", "/user/settings/actions/general", map[string]string{
|
||||
"cross_repo_add_target": "true",
|
||||
"cross_repo_add_target_name": repo2.Name,
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// create the runner for repo1
|
||||
runner1 := newMockRunner()
|
||||
runner1.registerAsRepoRunner(t, user2.Name, repo1.Name, "mock-runner", []string{"ubuntu-latest"}, false)
|
||||
|
||||
// set repo1 actions token permission mode to "permissive"
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", user2.Name, repo1.Name), map[string]string{
|
||||
"token_permission_mode": "permissive",
|
||||
"override_owner_config": "true",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// set repo2 actions token permission mode to "restricted", and set max permissions
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", user2.Name, repo2.Name), map[string]string{
|
||||
"token_permission_mode": "restricted",
|
||||
"override_owner_config": "true",
|
||||
"enable_max_permissions": "true",
|
||||
"max_unit_access_mode_" + strconv.Itoa(int(unit_model.TypeReleases)): "read",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// create a workflow file with "permission" keyword for repo1
|
||||
wfTreePath := ".gitea/workflows/test_permissions.yml"
|
||||
wfFileContent := `name: Test Permissions
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.gitea/workflows/test_permissions.yml'
|
||||
|
||||
jobs:
|
||||
job-override:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
code: write
|
||||
steps:
|
||||
- run: echo "test perms"
|
||||
`
|
||||
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
|
||||
createWorkflowFile(t, token, user2.Name, repo1.Name, wfTreePath, opts)
|
||||
|
||||
task1 := runner1.fetchTask(t)
|
||||
task1Token := task1.Secrets["GITEA_TOKEN"]
|
||||
require.NotEmpty(t, task1Token)
|
||||
|
||||
// should fail: target repo does not allow code access
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo2.Name)).AddTokenAuth(task1Token)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// set repo2 max permission to "read" so that the actions token can access code
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", user2.Name, repo2.Name), map[string]string{
|
||||
"token_permission_mode": "restricted",
|
||||
"override_owner_config": "true",
|
||||
"enable_max_permissions": "true",
|
||||
"max_unit_access_mode_" + strconv.Itoa(int(unit_model.TypeCode)): "read",
|
||||
"max_unit_access_mode_" + strconv.Itoa(int(unit_model.TypeReleases)): "read",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// should succeed: target repo now allows code read access for this token
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s", user2.Name, repo2.Name)).AddTokenAuth(task1Token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
// but it should not have write access
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/%s.git/info/lfs/objects/batch", user2.Name, repo2.Name), lfs.BatchRequest{Operation: "upload"}).
|
||||
SetHeader("Accept", lfs.MediaType).
|
||||
AddBasicAuth("gitea-actions", task1Token)
|
||||
MakeRequest(t, req, http.StatusUnauthorized)
|
||||
|
||||
// set repo1&repo2 max permission to "write" so that the actions token can access code
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", user2.Name, repo1.Name), map[string]string{
|
||||
"token_permission_mode": "restricted",
|
||||
"override_owner_config": "true",
|
||||
"enable_max_permissions": "true",
|
||||
"max_unit_access_mode_" + strconv.Itoa(int(unit_model.TypeCode)): "write",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", user2.Name, repo2.Name), map[string]string{
|
||||
"token_permission_mode": "restricted",
|
||||
"override_owner_config": "true",
|
||||
"enable_max_permissions": "true",
|
||||
"max_unit_access_mode_" + strconv.Itoa(int(unit_model.TypeCode)): "write",
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// now task1 has write access to repo1, but still only read access to repo2 (different repo)
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/%s.git/info/lfs/objects/batch", user2.Name, repo1.Name), lfs.BatchRequest{Operation: "upload"}).
|
||||
SetHeader("Accept", lfs.MediaType).
|
||||
AddBasicAuth("gitea-actions", task1Token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s/%s.git/info/lfs/objects/batch", user2.Name, repo2.Name), lfs.BatchRequest{Operation: "upload"}).
|
||||
SetHeader("Accept", lfs.MediaType).
|
||||
AddBasicAuth("gitea-actions", task1Token)
|
||||
MakeRequest(t, req, http.StatusUnauthorized)
|
||||
})
|
||||
}
|
||||
@@ -95,13 +95,13 @@ func (r *mockRunner) registerAsRepoRunner(t *testing.T, ownerName, repoName, run
|
||||
|
||||
func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1.Task {
|
||||
task := r.tryFetchTask(t, timeout...)
|
||||
assert.NotNil(t, task, "failed to fetch a task")
|
||||
require.NotNil(t, task, "failed to fetch a task")
|
||||
return task
|
||||
}
|
||||
|
||||
func (r *mockRunner) fetchNoTask(t *testing.T, timeout ...time.Duration) {
|
||||
task := r.tryFetchTask(t, timeout...)
|
||||
assert.Nil(t, task, "a task is fetched")
|
||||
require.Nil(t, task, "a task is fetched")
|
||||
}
|
||||
|
||||
const defaultFetchTaskTimeout = 1 * time.Second
|
||||
|
||||
@@ -29,7 +29,7 @@ func enableRepoDependencies(t *testing.T, repoID int64) {
|
||||
|
||||
repoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{RepoID: repoID, Type: unit.TypeIssues})
|
||||
repoUnit.IssuesConfig().EnableDependencies = true
|
||||
assert.NoError(t, repo_model.UpdateRepoUnit(t.Context(), repoUnit))
|
||||
assert.NoError(t, repo_model.UpdateRepoUnitConfig(t.Context(), repoUnit))
|
||||
}
|
||||
|
||||
func TestAPICreateIssueDependencyCrossRepoPermission(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ package integration
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
@@ -326,9 +327,10 @@ func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *Re
|
||||
urlStr = "/" + urlStr
|
||||
}
|
||||
req, err := http.NewRequest(method, urlStr, body)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = urlStr
|
||||
|
||||
require.NoError(t, err)
|
||||
if req.URL.User != nil {
|
||||
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(req.URL.User.String())))
|
||||
}
|
||||
return &RequestWrapper{req}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ func enableRepoAllowUpdateWithRebase(t *testing.T, repoID int64, allow bool) {
|
||||
|
||||
repoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{RepoID: repoID, Type: unit.TypePullRequests})
|
||||
repoUnit.PullRequestsConfig().AllowRebaseUpdate = allow
|
||||
assert.NoError(t, repo_model.UpdateRepoUnit(t.Context(), repoUnit))
|
||||
assert.NoError(t, repo_model.UpdateRepoUnitConfig(t.Context(), repoUnit))
|
||||
}
|
||||
|
||||
func TestAPIPullUpdateByRebase(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user