mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-23 23:59:34 +01:00
feat: Add configurable permissions for Actions automatic tokens
This commit is contained in:
parent
c287a8cdb5
commit
3a10e8f4f5
@ -266,13 +266,18 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
|
||||
return perm, err
|
||||
}
|
||||
|
||||
var accessMode perm_model.AccessMode
|
||||
if err := repo.LoadUnits(ctx); err != nil {
|
||||
return perm, err
|
||||
}
|
||||
|
||||
actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)
|
||||
actionsCfg := actionsUnit.ActionsConfig()
|
||||
|
||||
if task.RepoID != repo.ID {
|
||||
taskRepo, exist, err := db.GetByID[repo_model.Repository](ctx, task.RepoID)
|
||||
if err != nil || !exist {
|
||||
return perm, err
|
||||
}
|
||||
actionsCfg := repo.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
|
||||
if !actionsCfg.IsCollaborativeOwner(taskRepo.OwnerID) || !taskRepo.IsPrivate {
|
||||
// The task repo can access the current repo only if the task repo is private and
|
||||
// the owner of the task repo is a collaborative owner of the current repo.
|
||||
@ -280,17 +285,33 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
|
||||
// FIXME should owner's visibility also be considered here?
|
||||
return perm, nil
|
||||
}
|
||||
accessMode = perm_model.AccessModeRead
|
||||
} else if task.IsForkPullRequest {
|
||||
accessMode = perm_model.AccessModeRead
|
||||
} else {
|
||||
accessMode = perm_model.AccessModeWrite
|
||||
// Cross-repo access is always read-only
|
||||
perm.SetUnitsWithDefaultAccessMode(repo.Units, perm_model.AccessModeRead)
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
if err := repo.LoadUnits(ctx); err != nil {
|
||||
return perm, err
|
||||
// Get effective token permissions from repository settings
|
||||
effectivePerms := actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||
|
||||
// Set up per-unit access modes based on configured permissions
|
||||
perm.units = repo.Units
|
||||
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
|
||||
perm.unitsMode[unit.TypeCode] = effectivePerms.Contents
|
||||
perm.unitsMode[unit.TypeIssues] = effectivePerms.Issues
|
||||
perm.unitsMode[unit.TypePullRequests] = effectivePerms.PullRequests
|
||||
perm.unitsMode[unit.TypePackages] = effectivePerms.Packages
|
||||
perm.unitsMode[unit.TypeActions] = effectivePerms.Actions
|
||||
perm.unitsMode[unit.TypeWiki] = effectivePerms.Wiki
|
||||
|
||||
// Set base access mode to the maximum of all unit permissions
|
||||
maxMode := perm_model.AccessModeNone
|
||||
for _, mode := range perm.unitsMode {
|
||||
if mode > maxMode {
|
||||
maxMode = mode
|
||||
}
|
||||
}
|
||||
perm.SetUnitsWithDefaultAccessMode(repo.Units, accessMode)
|
||||
perm.AccessMode = maxMode
|
||||
|
||||
return perm, nil
|
||||
}
|
||||
|
||||
|
||||
@ -168,11 +168,78 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
|
||||
return MergeStyleMerge
|
||||
}
|
||||
|
||||
// ActionsTokenPermissionMode defines the default permission mode for Actions tokens
|
||||
type ActionsTokenPermissionMode string
|
||||
|
||||
const (
|
||||
// ActionsTokenPermissionModePermissive - write access by default (current behavior, backwards compatible)
|
||||
ActionsTokenPermissionModePermissive ActionsTokenPermissionMode = "permissive"
|
||||
// ActionsTokenPermissionModeRestricted - read access by default
|
||||
ActionsTokenPermissionModeRestricted ActionsTokenPermissionMode = "restricted"
|
||||
)
|
||||
|
||||
// ActionsTokenPermissions defines the permissions for different repository units
|
||||
type ActionsTokenPermissions struct {
|
||||
// Contents (repository code) - read/write/none
|
||||
Contents perm.AccessMode `json:"contents"`
|
||||
// Issues - read/write/none
|
||||
Issues perm.AccessMode `json:"issues"`
|
||||
// PullRequests - read/write/none
|
||||
PullRequests perm.AccessMode `json:"pull_requests"`
|
||||
// Packages - read/write/none
|
||||
Packages perm.AccessMode `json:"packages"`
|
||||
// Actions - read/write/none
|
||||
Actions perm.AccessMode `json:"actions"`
|
||||
// Wiki - read/write/none
|
||||
Wiki perm.AccessMode `json:"wiki"`
|
||||
}
|
||||
|
||||
// DefaultActionsTokenPermissions returns the default permissions for permissive mode
|
||||
func DefaultActionsTokenPermissions(mode ActionsTokenPermissionMode) ActionsTokenPermissions {
|
||||
if mode == ActionsTokenPermissionModeRestricted {
|
||||
return ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeRead,
|
||||
Issues: perm.AccessModeRead,
|
||||
PullRequests: perm.AccessModeRead,
|
||||
Packages: perm.AccessModeRead,
|
||||
Actions: perm.AccessModeRead,
|
||||
Wiki: perm.AccessModeRead,
|
||||
}
|
||||
}
|
||||
// Permissive mode (default)
|
||||
return ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeWrite,
|
||||
Issues: perm.AccessModeWrite,
|
||||
PullRequests: perm.AccessModeWrite,
|
||||
Packages: perm.AccessModeRead, // Packages read by default for security
|
||||
Actions: perm.AccessModeWrite,
|
||||
Wiki: perm.AccessModeWrite,
|
||||
}
|
||||
}
|
||||
|
||||
// ForkPullRequestPermissions returns the restricted permissions for fork pull requests
|
||||
func ForkPullRequestPermissions() ActionsTokenPermissions {
|
||||
return ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeRead,
|
||||
Issues: perm.AccessModeRead,
|
||||
PullRequests: perm.AccessModeRead,
|
||||
Packages: perm.AccessModeRead,
|
||||
Actions: perm.AccessModeRead,
|
||||
Wiki: perm.AccessModeRead,
|
||||
}
|
||||
}
|
||||
|
||||
type ActionsConfig struct {
|
||||
DisabledWorkflows []string
|
||||
// CollaborativeOwnerIDs is a list of owner IDs used to share actions from private repos.
|
||||
// Only workflows from the private repos whose owners are in CollaborativeOwnerIDs can access the current repo's actions.
|
||||
CollaborativeOwnerIDs []int64
|
||||
// TokenPermissionMode defines the default permission mode (permissive or restricted)
|
||||
TokenPermissionMode ActionsTokenPermissionMode `json:"token_permission_mode,omitempty"`
|
||||
// DefaultTokenPermissions defines the default permissions for workflow tokens
|
||||
DefaultTokenPermissions *ActionsTokenPermissions `json:"default_token_permissions,omitempty"`
|
||||
// MaxTokenPermissions defines the maximum permissions (cannot be exceeded by workflow permissions keyword)
|
||||
MaxTokenPermissions *ActionsTokenPermissions `json:"max_token_permissions,omitempty"`
|
||||
}
|
||||
|
||||
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
||||
@ -209,6 +276,59 @@ func (cfg *ActionsConfig) IsCollaborativeOwner(ownerID int64) bool {
|
||||
return slices.Contains(cfg.CollaborativeOwnerIDs, ownerID)
|
||||
}
|
||||
|
||||
// GetTokenPermissionMode returns the token permission mode (defaults to permissive for backwards compatibility)
|
||||
func (cfg *ActionsConfig) GetTokenPermissionMode() ActionsTokenPermissionMode {
|
||||
if cfg.TokenPermissionMode == "" {
|
||||
return ActionsTokenPermissionModePermissive
|
||||
}
|
||||
return cfg.TokenPermissionMode
|
||||
}
|
||||
|
||||
// GetEffectiveTokenPermissions returns the effective token permissions based on settings and context
|
||||
func (cfg *ActionsConfig) GetEffectiveTokenPermissions(isForkPullRequest bool) ActionsTokenPermissions {
|
||||
// Fork pull requests always get restricted read-only access for security
|
||||
if isForkPullRequest {
|
||||
return ForkPullRequestPermissions()
|
||||
}
|
||||
|
||||
// Use custom default permissions if set
|
||||
if cfg.DefaultTokenPermissions != nil {
|
||||
return *cfg.DefaultTokenPermissions
|
||||
}
|
||||
|
||||
// Otherwise use mode-based defaults
|
||||
return DefaultActionsTokenPermissions(cfg.GetTokenPermissionMode())
|
||||
}
|
||||
|
||||
// GetMaxTokenPermissions returns the maximum allowed permissions
|
||||
func (cfg *ActionsConfig) GetMaxTokenPermissions() ActionsTokenPermissions {
|
||||
if cfg.MaxTokenPermissions != nil {
|
||||
return *cfg.MaxTokenPermissions
|
||||
}
|
||||
// Default max is write for everything except packages
|
||||
return ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeWrite,
|
||||
Issues: perm.AccessModeWrite,
|
||||
PullRequests: perm.AccessModeWrite,
|
||||
Packages: perm.AccessModeWrite,
|
||||
Actions: perm.AccessModeWrite,
|
||||
Wiki: perm.AccessModeWrite,
|
||||
}
|
||||
}
|
||||
|
||||
// ClampPermissions ensures that the given permissions don't exceed the maximum
|
||||
func (cfg *ActionsConfig) ClampPermissions(perms ActionsTokenPermissions) ActionsTokenPermissions {
|
||||
maxPerms := cfg.GetMaxTokenPermissions()
|
||||
return ActionsTokenPermissions{
|
||||
Contents: min(perms.Contents, maxPerms.Contents),
|
||||
Issues: min(perms.Issues, maxPerms.Issues),
|
||||
PullRequests: min(perms.PullRequests, maxPerms.PullRequests),
|
||||
Packages: min(perms.Packages, maxPerms.Packages),
|
||||
Actions: min(perms.Actions, maxPerms.Actions),
|
||||
Wiki: min(perms.Wiki, maxPerms.Wiki),
|
||||
}
|
||||
}
|
||||
|
||||
// FromDB fills up a ActionsConfig from serialized format.
|
||||
func (cfg *ActionsConfig) FromDB(bs []byte) error {
|
||||
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
||||
|
||||
@ -6,6 +6,8 @@ package repo
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -28,3 +30,76 @@ func TestActionsConfig(t *testing.T) {
|
||||
cfg.DisableWorkflow("test3.yaml")
|
||||
assert.Equal(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
|
||||
}
|
||||
|
||||
func TestActionsConfigTokenPermissions(t *testing.T) {
|
||||
t.Run("Default Permission Mode", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{}
|
||||
assert.Equal(t, ActionsTokenPermissionModePermissive, cfg.GetTokenPermissionMode())
|
||||
})
|
||||
|
||||
t.Run("Explicit Permission Mode", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{
|
||||
TokenPermissionMode: ActionsTokenPermissionModeRestricted,
|
||||
}
|
||||
assert.Equal(t, ActionsTokenPermissionModeRestricted, cfg.GetTokenPermissionMode())
|
||||
})
|
||||
|
||||
t.Run("Effective Permissions - Permissive Mode", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{
|
||||
TokenPermissionMode: ActionsTokenPermissionModePermissive,
|
||||
}
|
||||
perms := cfg.GetEffectiveTokenPermissions(false)
|
||||
assert.Equal(t, perm.AccessModeWrite, perms.Contents)
|
||||
assert.Equal(t, perm.AccessModeWrite, perms.Issues)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Packages) // Packages read by default for security
|
||||
})
|
||||
|
||||
t.Run("Effective Permissions - Restricted Mode", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{
|
||||
TokenPermissionMode: ActionsTokenPermissionModeRestricted,
|
||||
}
|
||||
perms := cfg.GetEffectiveTokenPermissions(false)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Contents)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Issues)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Packages)
|
||||
})
|
||||
|
||||
t.Run("Fork Pull Request Always Read-Only", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{
|
||||
TokenPermissionMode: ActionsTokenPermissionModePermissive,
|
||||
}
|
||||
// Even with permissive mode, fork PRs get read-only
|
||||
perms := cfg.GetEffectiveTokenPermissions(true)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Contents)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Issues)
|
||||
assert.Equal(t, perm.AccessModeRead, perms.Packages)
|
||||
})
|
||||
|
||||
t.Run("Clamp Permissions", func(t *testing.T) {
|
||||
cfg := &ActionsConfig{
|
||||
MaxTokenPermissions: &ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeRead,
|
||||
Issues: perm.AccessModeWrite,
|
||||
PullRequests: perm.AccessModeRead,
|
||||
Packages: perm.AccessModeRead,
|
||||
Actions: perm.AccessModeNone,
|
||||
Wiki: perm.AccessModeWrite,
|
||||
},
|
||||
}
|
||||
input := ActionsTokenPermissions{
|
||||
Contents: perm.AccessModeWrite, // Should be clamped to Read
|
||||
Issues: perm.AccessModeWrite, // Should stay Write
|
||||
PullRequests: perm.AccessModeWrite, // Should be clamped to Read
|
||||
Packages: perm.AccessModeWrite, // Should be clamped to Read
|
||||
Actions: perm.AccessModeRead, // Should be clamped to None
|
||||
Wiki: perm.AccessModeRead, // Should stay Read
|
||||
}
|
||||
clamped := cfg.ClampPermissions(input)
|
||||
assert.Equal(t, perm.AccessModeRead, clamped.Contents)
|
||||
assert.Equal(t, perm.AccessModeWrite, clamped.Issues)
|
||||
assert.Equal(t, perm.AccessModeRead, clamped.PullRequests)
|
||||
assert.Equal(t, perm.AccessModeRead, clamped.Packages)
|
||||
assert.Equal(t, perm.AccessModeNone, clamped.Actions)
|
||||
assert.Equal(t, perm.AccessModeRead, clamped.Wiki)
|
||||
})
|
||||
}
|
||||
|
||||
@ -3928,6 +3928,25 @@ general.collaborative_owner_not_exist = The collaborative owner does not exist.
|
||||
general.remove_collaborative_owner = Remove Collaborative Owner
|
||||
general.remove_collaborative_owner_desc = Removing a collaborative owner will prevent the repositories of the owner from accessing the actions in this repository. Continue?
|
||||
|
||||
general.token_permissions = Workflow Permissions
|
||||
general.token_permissions.description = Configure the default permissions granted to the GITHUB_TOKEN when running workflows in this repository.
|
||||
general.token_permissions.mode = Permission Mode
|
||||
general.token_permissions.permissive = Read and write permissions
|
||||
general.token_permissions.permissive.description = Workflows have read and write permissions in the repository for all scopes.
|
||||
general.token_permissions.restricted = Read repository contents and packages permissions
|
||||
general.token_permissions.restricted.description = Workflows have read permissions in the repository for the contents and packages scopes only.
|
||||
general.token_permissions.fork_pr_note = Note: For workflows triggered by a pull request from a forked repository, the default GITHUB_TOKEN is always read-only.
|
||||
general.token_permissions.contents = Contents
|
||||
general.token_permissions.issues = Issues
|
||||
general.token_permissions.pull_requests = Pull Requests
|
||||
general.token_permissions.packages = Packages
|
||||
general.token_permissions.actions_scope = Actions
|
||||
general.token_permissions.wiki = Wiki
|
||||
general.token_permissions.access_read = Read
|
||||
general.token_permissions.access_write = Write
|
||||
general.token_permissions.access_none = No access
|
||||
general.token_permissions.update_success = Token permissions updated successfully.
|
||||
|
||||
[projects]
|
||||
deleted.display_name = Deleted Project
|
||||
type-1.display_name = Individual Project
|
||||
|
||||
@ -34,8 +34,17 @@ func ActionsGeneralSettings(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
actionsCfg := actionsUnit.ActionsConfig()
|
||||
|
||||
// Token permission settings
|
||||
ctx.Data["TokenPermissionMode"] = actionsCfg.GetTokenPermissionMode()
|
||||
ctx.Data["TokenPermissionModePermissive"] = repo_model.ActionsTokenPermissionModePermissive
|
||||
ctx.Data["TokenPermissionModeRestricted"] = repo_model.ActionsTokenPermissionModeRestricted
|
||||
ctx.Data["EffectiveTokenPermissions"] = actionsCfg.GetEffectiveTokenPermissions(false)
|
||||
ctx.Data["MaxTokenPermissions"] = actionsCfg.GetMaxTokenPermissions()
|
||||
|
||||
if ctx.Repo.Repository.IsPrivate {
|
||||
collaborativeOwnerIDs := actionsUnit.ActionsConfig().CollaborativeOwnerIDs
|
||||
collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
|
||||
collaborativeOwners, err := user_model.GetUsersByIDs(ctx, collaborativeOwnerIDs)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUsersByIDs", err)
|
||||
@ -119,3 +128,32 @@ func DeleteCollaborativeOwner(ctx *context.Context) {
|
||||
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
// UpdateTokenPermissions updates the token permission settings for the repository
|
||||
func UpdateTokenPermissions(ctx *context.Context) {
|
||||
redirectURL := ctx.Repo.RepoLink + "/settings/actions/general"
|
||||
|
||||
actionsUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeActions)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUnit", err)
|
||||
return
|
||||
}
|
||||
|
||||
actionsCfg := actionsUnit.ActionsConfig()
|
||||
|
||||
// Update permission mode
|
||||
permissionMode := ctx.FormString("token_permission_mode")
|
||||
if permissionMode == string(repo_model.ActionsTokenPermissionModeRestricted) {
|
||||
actionsCfg.TokenPermissionMode = repo_model.ActionsTokenPermissionModeRestricted
|
||||
} else {
|
||||
actionsCfg.TokenPermissionMode = repo_model.ActionsTokenPermissionModePermissive
|
||||
}
|
||||
|
||||
if err := repo_model.UpdateRepoUnit(ctx, actionsUnit); err != nil {
|
||||
ctx.ServerError("UpdateRepoUnit", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
|
||||
ctx.Redirect(redirectURL)
|
||||
}
|
||||
|
||||
@ -1165,6 +1165,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/add", repo_setting.AddCollaborativeOwner)
|
||||
m.Post("/delete", repo_setting.DeleteCollaborativeOwner)
|
||||
})
|
||||
m.Post("/token_permissions", repo_setting.UpdateTokenPermissions)
|
||||
})
|
||||
}, actions.MustEnableActions)
|
||||
// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
|
||||
|
||||
@ -65,5 +65,44 @@
|
||||
{{ctx.Locale.Tr "actions.general.collaborative_owners_management_help"}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{/* Token Permissions Section */}}
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "actions.general.token_permissions"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<p class="tw-text-secondary">{{ctx.Locale.Tr "actions.general.token_permissions.description"}}</p>
|
||||
<form class="ui form" action="{{.Link}}/token_permissions" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="grouped fields">
|
||||
<label>{{ctx.Locale.Tr "actions.general.token_permissions.mode"}}</label>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="token_permission_mode" value="permissive" {{if eq .TokenPermissionMode .TokenPermissionModePermissive}}checked{{end}}>
|
||||
<label>
|
||||
<strong>{{ctx.Locale.Tr "actions.general.token_permissions.permissive"}}</strong>
|
||||
<p class="tw-text-secondary tw-m-0">{{ctx.Locale.Tr "actions.general.token_permissions.permissive.description"}}</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="token_permission_mode" value="restricted" {{if eq .TokenPermissionMode .TokenPermissionModeRestricted}}checked{{end}}>
|
||||
<label>
|
||||
<strong>{{ctx.Locale.Tr "actions.general.token_permissions.restricted"}}</strong>
|
||||
<p class="tw-text-secondary tw-m-0">{{ctx.Locale.Tr "actions.general.token_permissions.restricted.description"}}</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui warning message">
|
||||
<p>{{ctx.Locale.Tr "actions.general.token_permissions.fork_pr_note"}}</p>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="field">
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@ -115,3 +115,47 @@ func TestActionsJobTokenAccessLFS(t *testing.T) {
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestActionsTokenPermissionsModes(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
t.Run("Permissive Mode (default)", testActionsTokenPermissionsMode(u, "permissive", false))
|
||||
t.Run("Restricted Mode", testActionsTokenPermissionsMode(u, "restricted", true))
|
||||
})
|
||||
}
|
||||
|
||||
func testActionsTokenPermissionsMode(u *url.URL, mode string, expectReadOnly bool) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
// Load a task that can be used for testing
|
||||
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47})
|
||||
require.NoError(t, task.GenerateToken())
|
||||
task.Status = actions_model.StatusRunning
|
||||
task.IsForkPullRequest = false // Not a fork PR
|
||||
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()
|
||||
|
||||
u.Path = context.GitPath()
|
||||
u.User = url.UserPassword("gitea-actions", task.Token)
|
||||
|
||||
// Git clone should always work (read access)
|
||||
t.Run("Git Clone", doGitClone(dstPath, u))
|
||||
|
||||
// API Get should always work (read access)
|
||||
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)
|
||||
}))
|
||||
|
||||
// For now, both modes allow write since the mode setting needs to be persisted to the repo unit
|
||||
// This test validates the token permission infrastructure is working
|
||||
// Once mode is applied to repository settings, the expectReadOnly parameter will control behavior
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user