mirror of
https://github.com/go-gitea/gitea.git
synced 2026-02-15 05:52:32 +01:00
Implement Workflow Level Permissions
This commit is contained in:
parent
f367039e78
commit
1ff75aa822
@ -51,6 +51,10 @@ type ActionRunJob struct {
|
||||
ConcurrencyGroup string `xorm:"index(repo_concurrency) NOT NULL DEFAULT ''"` // evaluated concurrency.group
|
||||
ConcurrencyCancel bool `xorm:"NOT NULL DEFAULT FALSE"` // evaluated concurrency.cancel-in-progress
|
||||
|
||||
// TokenPermissions stores the parsed permissions from the workflow YAML (workflow + job level, clamped by repo max settings)
|
||||
// This is JSON-encoded repo_model.ActionsTokenPermissions
|
||||
TokenPermissions string `xorm:"TEXT"`
|
||||
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
|
||||
@ -325,9 +325,25 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
// Get effective token permissions from repository settings
|
||||
effectivePerms := actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
|
||||
// Get effective token permissions
|
||||
// First check if job has explicit permissions stored from workflow YAML
|
||||
var effectivePerms repo_model.ActionsTokenPermissions
|
||||
if err := task.LoadJob(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
if task.Job != nil && task.Job.TokenPermissions != "" {
|
||||
// Use permissions parsed from workflow YAML (already clamped by repo max settings during insertion)
|
||||
effectivePerms, err = repo_model.UnmarshalTokenPermissions(task.Job.TokenPermissions)
|
||||
if err != nil {
|
||||
// Fall back to repository settings if unmarshal fails
|
||||
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
|
||||
}
|
||||
} else {
|
||||
// No workflow permissions, use repository settings
|
||||
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
|
||||
}
|
||||
|
||||
// Set up per-unit access modes based on configured permissions
|
||||
perm.units = repo.Units
|
||||
|
||||
@ -261,6 +261,25 @@ func ForkPullRequestPermissions() ActionsTokenPermissions {
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalTokenPermissions serializes ActionsTokenPermissions to JSON
|
||||
func MarshalTokenPermissions(perms ActionsTokenPermissions) string {
|
||||
data, err := json.Marshal(perms)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// UnmarshalTokenPermissions deserializes JSON to ActionsTokenPermissions
|
||||
func UnmarshalTokenPermissions(data string) (ActionsTokenPermissions, error) {
|
||||
var perms ActionsTokenPermissions
|
||||
if data == "" {
|
||||
return perms, nil
|
||||
}
|
||||
err := json.Unmarshal([]byte(data), &perms)
|
||||
return perms, err
|
||||
}
|
||||
|
||||
type ActionsConfig struct {
|
||||
DisabledWorkflows []string
|
||||
// CollaborativeOwnerIDs is a list of owner IDs used to share actions from private repos.
|
||||
|
||||
133
services/actions/permission_parser.go
Normal file
133
services/actions/permission_parser.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
|
||||
"github.com/nektos/act/pkg/jobparser"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ParseWorkflowPermissions extracts workflow-level permissions from a SingleWorkflow
|
||||
// Returns the default permissions based on repository settings if no workflow permissions are specified
|
||||
func ParseWorkflowPermissions(wf *jobparser.SingleWorkflow, defaultPerms repo_model.ActionsTokenPermissions) repo_model.ActionsTokenPermissions {
|
||||
if wf == nil {
|
||||
return defaultPerms
|
||||
}
|
||||
|
||||
// Check if workflow has RawPermissions
|
||||
rawPerms := wf.RawPermissions
|
||||
if rawPerms.Kind == yaml.ScalarNode && rawPerms.Value == "" {
|
||||
return defaultPerms
|
||||
}
|
||||
|
||||
return parseRawPermissions(&rawPerms, defaultPerms)
|
||||
}
|
||||
|
||||
// ParseJobPermissions extracts job-level permissions, falling back to workflow defaults
|
||||
func ParseJobPermissions(job *jobparser.Job, workflowPerms repo_model.ActionsTokenPermissions) repo_model.ActionsTokenPermissions {
|
||||
if job == nil {
|
||||
return workflowPerms
|
||||
}
|
||||
|
||||
// Check if job has RawPermissions
|
||||
rawPerms := job.RawPermissions
|
||||
if rawPerms.Kind == yaml.ScalarNode && rawPerms.Value == "" {
|
||||
return workflowPerms
|
||||
}
|
||||
|
||||
return parseRawPermissions(&rawPerms, workflowPerms)
|
||||
}
|
||||
|
||||
// parseRawPermissions parses a YAML permissions node into ActionsTokenPermissions
|
||||
func parseRawPermissions(rawPerms *yaml.Node, defaultPerms repo_model.ActionsTokenPermissions) repo_model.ActionsTokenPermissions {
|
||||
if rawPerms == nil || (rawPerms.Kind == yaml.ScalarNode && rawPerms.Value == "") {
|
||||
return defaultPerms
|
||||
}
|
||||
|
||||
// Handle scalar values: "read-all" or "write-all"
|
||||
if rawPerms.Kind == yaml.ScalarNode {
|
||||
switch rawPerms.Value {
|
||||
case "read-all":
|
||||
return repo_model.ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeRead,
|
||||
Issues: perm.AccessModeRead,
|
||||
PullRequests: perm.AccessModeRead,
|
||||
Packages: perm.AccessModeRead,
|
||||
Actions: perm.AccessModeRead,
|
||||
Wiki: perm.AccessModeRead,
|
||||
}
|
||||
case "write-all":
|
||||
return repo_model.ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeWrite,
|
||||
Issues: perm.AccessModeWrite,
|
||||
PullRequests: perm.AccessModeWrite,
|
||||
Packages: perm.AccessModeWrite,
|
||||
Actions: perm.AccessModeWrite,
|
||||
Wiki: perm.AccessModeWrite,
|
||||
}
|
||||
}
|
||||
return defaultPerms
|
||||
}
|
||||
|
||||
// Handle mapping: individual permission scopes
|
||||
if rawPerms.Kind == yaml.MappingNode {
|
||||
result := defaultPerms // Start with defaults
|
||||
|
||||
for i := 0; i < len(rawPerms.Content); i += 2 {
|
||||
if i+1 >= len(rawPerms.Content) {
|
||||
break
|
||||
}
|
||||
keyNode := rawPerms.Content[i]
|
||||
valueNode := rawPerms.Content[i+1]
|
||||
|
||||
if keyNode.Kind != yaml.ScalarNode || valueNode.Kind != yaml.ScalarNode {
|
||||
continue
|
||||
}
|
||||
|
||||
scope := keyNode.Value
|
||||
accessStr := valueNode.Value
|
||||
accessMode := parseAccessMode(accessStr)
|
||||
|
||||
// Map GitHub Actions scopes to Gitea units
|
||||
switch scope {
|
||||
case "contents":
|
||||
result.Contents = accessMode
|
||||
case "issues":
|
||||
result.Issues = accessMode
|
||||
case "pull-requests":
|
||||
result.PullRequests = accessMode
|
||||
case "packages":
|
||||
result.Packages = accessMode
|
||||
case "actions":
|
||||
result.Actions = accessMode
|
||||
case "wiki":
|
||||
result.Wiki = accessMode
|
||||
// Additional GitHub scopes we don't explicitly handle yet:
|
||||
// These fall through to defaults
|
||||
// - deployments, environments, id-token, pages, repository-projects, security-events, statuses
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return defaultPerms
|
||||
}
|
||||
|
||||
// parseAccessMode converts a string access level to perm.AccessMode
|
||||
func parseAccessMode(s string) perm.AccessMode {
|
||||
switch s {
|
||||
case "write":
|
||||
return perm.AccessModeWrite
|
||||
case "read":
|
||||
return perm.AccessModeRead
|
||||
case "none":
|
||||
return perm.AccessModeNone
|
||||
default:
|
||||
return perm.AccessModeNone
|
||||
}
|
||||
}
|
||||
169
services/actions/permission_parser_test.go
Normal file
169
services/actions/permission_parser_test.go
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestParseRawPermissions_ReadAll(t *testing.T) {
|
||||
var rawPerms yaml.Node
|
||||
err := yaml.Unmarshal([]byte(`read-all`), &rawPerms)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defaultPerms := repo_model.DefaultActionsTokenPermissions(repo_model.ActionsTokenPermissionModePermissive)
|
||||
result := parseRawPermissions(&rawPerms, defaultPerms)
|
||||
|
||||
assert.Equal(t, perm.AccessModeRead, result.Contents)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Issues)
|
||||
assert.Equal(t, perm.AccessModeRead, result.PullRequests)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Packages)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Actions)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Wiki)
|
||||
}
|
||||
|
||||
func TestParseRawPermissions_WriteAll(t *testing.T) {
|
||||
var rawPerms yaml.Node
|
||||
err := yaml.Unmarshal([]byte(`write-all`), &rawPerms)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defaultPerms := repo_model.DefaultActionsTokenPermissions(repo_model.ActionsTokenPermissionModeRestricted)
|
||||
result := parseRawPermissions(&rawPerms, defaultPerms)
|
||||
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Contents)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Issues)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.PullRequests)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Packages)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Actions)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Wiki)
|
||||
}
|
||||
|
||||
func TestParseRawPermissions_IndividualScopes(t *testing.T) {
|
||||
yamlContent := `
|
||||
contents: write
|
||||
issues: read
|
||||
pull-requests: none
|
||||
packages: write
|
||||
actions: read
|
||||
wiki: write
|
||||
`
|
||||
var rawPerms yaml.Node
|
||||
err := yaml.Unmarshal([]byte(yamlContent), &rawPerms)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defaultPerms := repo_model.ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeNone,
|
||||
Issues: perm.AccessModeNone,
|
||||
PullRequests: perm.AccessModeNone,
|
||||
Packages: perm.AccessModeNone,
|
||||
Actions: perm.AccessModeNone,
|
||||
Wiki: perm.AccessModeNone,
|
||||
}
|
||||
result := parseRawPermissions(&rawPerms, defaultPerms)
|
||||
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Contents)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Issues)
|
||||
assert.Equal(t, perm.AccessModeNone, result.PullRequests)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Packages)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Actions)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Wiki)
|
||||
}
|
||||
|
||||
func TestParseRawPermissions_PartialOverride(t *testing.T) {
|
||||
yamlContent := `
|
||||
contents: read
|
||||
issues: write
|
||||
`
|
||||
var rawPerms yaml.Node
|
||||
err := yaml.Unmarshal([]byte(yamlContent), &rawPerms)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Defaults are write for everything
|
||||
defaultPerms := repo_model.DefaultActionsTokenPermissions(repo_model.ActionsTokenPermissionModePermissive)
|
||||
result := parseRawPermissions(&rawPerms, defaultPerms)
|
||||
|
||||
// Overridden scopes
|
||||
assert.Equal(t, perm.AccessModeRead, result.Contents)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Issues)
|
||||
// Non-overridden scopes keep defaults
|
||||
assert.Equal(t, perm.AccessModeWrite, result.PullRequests)
|
||||
assert.Equal(t, perm.AccessModeRead, result.Packages) // Packages default to read in permissive
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Actions)
|
||||
assert.Equal(t, perm.AccessModeWrite, result.Wiki)
|
||||
}
|
||||
|
||||
func TestParseRawPermissions_EmptyNode(t *testing.T) {
|
||||
var rawPerms yaml.Node
|
||||
// Empty node
|
||||
|
||||
defaultPerms := repo_model.DefaultActionsTokenPermissions(repo_model.ActionsTokenPermissionModePermissive)
|
||||
result := parseRawPermissions(&rawPerms, defaultPerms)
|
||||
|
||||
// Should return defaults
|
||||
assert.Equal(t, defaultPerms.Contents, result.Contents)
|
||||
assert.Equal(t, defaultPerms.Issues, result.Issues)
|
||||
}
|
||||
|
||||
func TestParseRawPermissions_NilNode(t *testing.T) {
|
||||
defaultPerms := repo_model.DefaultActionsTokenPermissions(repo_model.ActionsTokenPermissionModePermissive)
|
||||
result := parseRawPermissions(nil, defaultPerms)
|
||||
|
||||
// Should return defaults
|
||||
assert.Equal(t, defaultPerms.Contents, result.Contents)
|
||||
assert.Equal(t, defaultPerms.Issues, result.Issues)
|
||||
}
|
||||
|
||||
func TestParseAccessMode(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected perm.AccessMode
|
||||
}{
|
||||
{"write", perm.AccessModeWrite},
|
||||
{"read", perm.AccessModeRead},
|
||||
{"none", perm.AccessModeNone},
|
||||
{"", perm.AccessModeNone},
|
||||
{"invalid", perm.AccessModeNone},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
result := parseAccessMode(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalTokenPermissions(t *testing.T) {
|
||||
original := repo_model.ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeWrite,
|
||||
Issues: perm.AccessModeRead,
|
||||
PullRequests: perm.AccessModeNone,
|
||||
Packages: perm.AccessModeWrite,
|
||||
Actions: perm.AccessModeRead,
|
||||
Wiki: perm.AccessModeWrite,
|
||||
}
|
||||
|
||||
// Marshal
|
||||
jsonStr := repo_model.MarshalTokenPermissions(original)
|
||||
assert.NotEmpty(t, jsonStr)
|
||||
|
||||
// Unmarshal
|
||||
result, err := repo_model.UnmarshalTokenPermissions(jsonStr)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, original, result)
|
||||
}
|
||||
|
||||
func TestUnmarshalTokenPermissions_EmptyString(t *testing.T) {
|
||||
result, err := repo_model.UnmarshalTokenPermissions("")
|
||||
assert.NoError(t, err)
|
||||
// Should return zero-value struct
|
||||
assert.Equal(t, perm.AccessModeNone, result.Contents)
|
||||
assert.Equal(t, perm.AccessModeNone, result.Issues)
|
||||
}
|
||||
@ -9,6 +9,8 @@ import (
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
unit_model "code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
|
||||
@ -103,6 +105,20 @@ func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobpar
|
||||
|
||||
runJobs := make([]*actions_model.ActionRunJob, 0, len(jobs))
|
||||
var hasWaitingJobs bool
|
||||
|
||||
// Load Actions configuration to get default and max permissions
|
||||
var actionsCfg *repo_model.ActionsConfig
|
||||
actionsUnit, err := run.Repo.GetUnit(ctx, unit_model.TypeActions)
|
||||
if err == nil {
|
||||
actionsCfg = actionsUnit.ActionsConfig()
|
||||
} else {
|
||||
// Default config if Actions unit doesn't exist
|
||||
actionsCfg = &repo_model.ActionsConfig{}
|
||||
}
|
||||
|
||||
// Get default permissions based on repository settings
|
||||
defaultPerms := actionsCfg.GetEffectiveTokenPermissions(run.IsForkPullRequest)
|
||||
|
||||
for _, v := range jobs {
|
||||
id, job := v.Job()
|
||||
needs := job.Needs()
|
||||
@ -113,6 +129,12 @@ func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobpar
|
||||
|
||||
shouldBlockJob := len(needs) > 0 || run.NeedApproval || run.Status == actions_model.StatusBlocked
|
||||
|
||||
// Parse workflow-level and job-level permissions
|
||||
workflowPerms := ParseWorkflowPermissions(v, defaultPerms)
|
||||
jobPerms := ParseJobPermissions(job, workflowPerms)
|
||||
// Clamp by repository max settings
|
||||
finalPerms := actionsCfg.ClampPermissions(jobPerms)
|
||||
|
||||
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
||||
runJob := &actions_model.ActionRunJob{
|
||||
RunID: run.ID,
|
||||
@ -126,6 +148,7 @@ func InsertRun(ctx context.Context, run *actions_model.ActionRun, jobs []*jobpar
|
||||
Needs: needs,
|
||||
RunsOn: job.RunsOn(),
|
||||
Status: util.Iif(shouldBlockJob, actions_model.StatusBlocked, actions_model.StatusWaiting),
|
||||
TokenPermissions: repo_model.MarshalTokenPermissions(finalPerms),
|
||||
}
|
||||
// check job concurrency
|
||||
if job.RawConcurrency != nil {
|
||||
|
||||
@ -514,3 +514,99 @@ func TestActionsTokenPermissionsWorkflowScenario(t *testing.T) {
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// TestActionsWorkflowPermissionsKeyword tests that the `permissions:` keyword in a workflow YAML
|
||||
// restricts the token even when the repository is in permissive mode.
|
||||
// This is exactly what the reviewer reported: `permissions: read-all` should restrict write operations.
|
||||
func TestActionsWorkflowPermissionsKeyword(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
httpContext := NewAPITestContext(t, "user2", "repo-workflow-perms-kw", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository)
|
||||
t.Run("Workflow Permissions Keyword", doAPICreateRepository(httpContext, false, func(t *testing.T, repository structs.Repository) {
|
||||
// Enable Actions unit with PERMISSIVE mode (default write access)
|
||||
err := db.Insert(t.Context(), &repo_model.RepoUnit{
|
||||
RepoID: repository.ID,
|
||||
Type: unit_model.TypeActions,
|
||||
Config: &repo_model.ActionsConfig{
|
||||
TokenPermissionMode: repo_model.ActionsTokenPermissionModePermissive,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an Actions run job with TokenPermissions set (simulating a workflow with permissions: read-all)
|
||||
// This is what the permission parser does when parsing the workflow YAML
|
||||
readOnlyPerms := repo_model.ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeRead,
|
||||
Issues: perm.AccessModeRead,
|
||||
PullRequests: perm.AccessModeRead,
|
||||
Packages: perm.AccessModeRead,
|
||||
Actions: perm.AccessModeRead,
|
||||
Wiki: perm.AccessModeRead,
|
||||
}
|
||||
permsJSON := repo_model.MarshalTokenPermissions(readOnlyPerms)
|
||||
|
||||
// Create a run and job with explicit permissions
|
||||
run := &actions_model.ActionRun{
|
||||
RepoID: repository.ID,
|
||||
OwnerID: repository.Owner.ID,
|
||||
Title: "Test workflow with read-all permissions",
|
||||
Status: actions_model.StatusRunning,
|
||||
Ref: "refs/heads/master",
|
||||
CommitSHA: "abc123",
|
||||
}
|
||||
require.NoError(t, db.Insert(t.Context(), run))
|
||||
|
||||
job := &actions_model.ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RepoID: repository.ID,
|
||||
OwnerID: repository.Owner.ID,
|
||||
CommitSHA: "abc123",
|
||||
Name: "test-job",
|
||||
JobID: "test-job",
|
||||
Status: actions_model.StatusRunning,
|
||||
TokenPermissions: permsJSON, // This is the key - workflow-declared permissions
|
||||
}
|
||||
require.NoError(t, db.Insert(t.Context(), job))
|
||||
|
||||
// Create task linked to the job
|
||||
task := &actions_model.ActionTask{
|
||||
JobID: job.ID,
|
||||
RepoID: repository.ID,
|
||||
Status: actions_model.StatusRunning,
|
||||
IsForkPullRequest: false,
|
||||
}
|
||||
require.NoError(t, task.GenerateToken())
|
||||
require.NoError(t, db.Insert(t.Context(), task))
|
||||
|
||||
// Update job with task ID
|
||||
job.TaskID = task.ID
|
||||
_, err = db.GetEngine(t.Context()).ID(job.ID).Cols("task_id").Update(job)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test: Even though repo is in PERMISSIVE mode, the workflow has permissions: read-all
|
||||
// So write operations should FAIL
|
||||
session := emptyTestSession(t)
|
||||
testCtx := APITestContext{
|
||||
Session: session,
|
||||
Token: task.Token,
|
||||
Username: "user2",
|
||||
Reponame: "repo-workflow-perms-kw",
|
||||
}
|
||||
|
||||
// Read should work
|
||||
testCtx.ExpectedCode = http.StatusOK
|
||||
t.Run("GITEA_TOKEN Get Repository (Read OK)", doAPIGetRepository(testCtx, func(t *testing.T, r structs.Repository) {
|
||||
assert.Equal(t, "repo-workflow-perms-kw", r.Name)
|
||||
}))
|
||||
|
||||
// Write should FAIL due to workflow permissions: read-all
|
||||
testCtx.ExpectedCode = http.StatusForbidden
|
||||
t.Run("GITEA_TOKEN Create File (Write BLOCKED by workflow permissions)", doAPICreateFile(testCtx, "should-fail-due-to-workflow-perms.txt", &structs.CreateFileOptions{
|
||||
FileOptions: structs.FileOptions{
|
||||
BranchName: "master",
|
||||
Message: "this should fail due to workflow permissions",
|
||||
},
|
||||
ContentBase64: base64.StdEncoding.EncodeToString([]byte("Should Not Be Created")),
|
||||
}))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user