0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-01-20 15:44:54 +01:00
This commit is contained in:
Excellencedev 2026-01-16 16:16:53 +01:00
parent feb791cccf
commit 7cccb84fdb
5 changed files with 57 additions and 31 deletions

View File

@ -346,13 +346,37 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
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)
// If following org config, we need to load it
if !actionsCfg.OverrideOrgConfig && repo.Owner.IsOrganization() {
orgCfg, err := actions_model.GetOrgActionsConfig(ctx, repo.OwnerID)
if err != nil {
log.Error("GetOrgActionsConfig: %v", err)
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest) // Fallback to repo config on error
} else {
effectivePerms = orgCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
effectivePerms = orgCfg.ClampPermissions(effectivePerms)
}
} else {
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
}
}
} else {
// No workflow permissions or job not found, use repository settings
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
if !actionsCfg.OverrideOrgConfig && repo.Owner.IsOrganization() {
orgCfg, err := actions_model.GetOrgActionsConfig(ctx, repo.OwnerID)
if err != nil {
log.Error("GetOrgActionsConfig: %v", err)
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest) // Fallback to repo config on error
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
} else {
effectivePerms = orgCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
effectivePerms = orgCfg.ClampPermissions(effectivePerms)
}
} else {
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
}
}
// Set up per-unit access modes based on configured permissions

View File

@ -310,8 +310,8 @@ type ActionsConfig struct {
AllowCrossRepoAccess bool `json:"allow_cross_repo_access,omitempty"`
// AllowedCrossRepoIDs is a list of specific repo IDs that can be accessed cross-repo (empty means all if AllowCrossRepoAccess is true)
AllowedCrossRepoIDs []int64 `json:"allowed_cross_repo_ids,omitempty"`
// FollowOrgConfig indicates if this repository should follow the organization-level configuration
FollowOrgConfig bool `json:"follow_org_config,omitempty"`
// OverrideOrgConfig indicates if this repository should override the organization-level configuration
OverrideOrgConfig bool `json:"override_org_config,omitempty"`
}
func (cfg *ActionsConfig) EnableWorkflow(file string) {

View File

@ -46,7 +46,7 @@ func ActionsGeneralSettings(ctx *context.Context) {
// Follow org config (only for repos in orgs)
ctx.Data["IsInOrg"] = ctx.Repo.Repository.Owner.IsOrganization()
ctx.Data["FollowOrgConfig"] = actionsCfg.FollowOrgConfig
ctx.Data["OverrideOrgConfig"] = actionsCfg.OverrideOrgConfig
if ctx.Repo.Repository.IsPrivate {
collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
@ -146,11 +146,12 @@ func UpdateTokenPermissions(ctx *context.Context) {
actionsCfg := actionsUnit.ActionsConfig()
// Update Follow Org Config (for repos in orgs)
actionsCfg.FollowOrgConfig = ctx.FormBool("follow_org_config")
// Update Override Org Config (for repos in orgs)
// If checked, it means we WANT to override (opt-out of following)
actionsCfg.OverrideOrgConfig = ctx.FormBool("override_org_config")
// Update permission mode (only if not following org config)
if !actionsCfg.FollowOrgConfig {
// Update permission mode (only if overriding org config)
if actionsCfg.OverrideOrgConfig {
permissionMode := repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode"))
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted ||
permissionMode == repo_model.ActionsTokenPermissionModePermissive ||
@ -164,7 +165,7 @@ func UpdateTokenPermissions(ctx *context.Context) {
}
// Update Maximum Permissions (radio buttons: none/read/write)
if actionsCfg.TokenPermissionMode == repo_model.ActionsTokenPermissionModeCustom {
if actionsCfg.OverrideOrgConfig && actionsCfg.TokenPermissionMode == repo_model.ActionsTokenPermissionModeCustom {
parseMaxPerm := func(name string) perm.AccessMode {
value := ctx.FormString("max_" + name)
switch value {

View File

@ -33,13 +33,13 @@
{{.CsrfTokenHtml}}
{{if .IsInOrg}}
<!-- Follow Organization Configuration -->
<!-- Override Organization Configuration -->
<div class="field">
<div class="ui checkbox">
<input type="checkbox" name="follow_org_config" id="follow-org-config" {{if .FollowOrgConfig}}checked{{end}} class="js-follow-org-config">
<label><strong>{{ctx.Locale.Tr "actions.general.token_permissions.follow_org"}}</strong></label>
<input type="checkbox" name="override_org_config" id="override-org-config" {{if .OverrideOrgConfig}}checked{{end}} class="js-follow-org-config">
<label><strong>{{ctx.Locale.Tr "actions.general.token_permissions.override_org"}}</strong></label>
</div>
<p class="help">{{ctx.Locale.Tr "actions.general.token_permissions.follow_org_desc"}}</p>
<p class="help">{{ctx.Locale.Tr "actions.general.token_permissions.override_org_desc"}}</p>
</div>
<div class="divider"></div>

View File

@ -177,7 +177,7 @@ func testActionsTokenPermissionsMode(u *url.URL, mode string, expectReadOnly boo
require.NoError(t, err, "Actions unit should exist for repo4")
actionsCfg := actionsUnit.ActionsConfig()
actionsCfg.TokenPermissionMode = repo_model.ActionsTokenPermissionMode(mode)
actionsCfg.MaxTokenPermissions = nil // Ensure no max permissions interfere
actionsCfg.MaxTokenPermissions = nil // Ensure no max permissions interfere
// Update the config
actionsUnit.Config = actionsCfg
require.NoError(t, repo_model.UpdateRepoUnit(t.Context(), actionsUnit))
@ -343,11 +343,12 @@ func TestActionsTokenPackagePermission(t *testing.T) {
runner := newMockRunner()
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
// Set Config: Custom Mode, Max Packages = Write
// This should implied Default Packages = Write (because Custom defaults to Max)
// Set Config: Custom Mode, Max Packages = Write, Max Code = Read
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", repo.OwnerName, repo.Name), map[string]string{
"override_org_config": "true",
"token_permission_mode": "custom",
"max_packages": "write",
"max_code": "read", // Ensure repo read access if needed
})
session.MakeRequest(t, req, http.StatusSeeOther)
@ -375,7 +376,7 @@ jobs:
writePackageURL := fmt.Sprintf("/api/packages/%s/generic/%s/%s/test.bin", user2.Name, packageName, packageVersion)
uploadReq := NewRequestWithBody(t, "PUT", writePackageURL, bytes.NewReader([]byte{1, 2, 3})).
AddTokenAuth(taskToken)
// Should Succeed (201)
MakeRequest(t, uploadReq, http.StatusCreated)
})
@ -441,26 +442,26 @@ func TestActionsCrossRepoAccess(t *testing.T) {
Reponame: "repo-B",
}
// Case A: Default (AllowCrossRepoAccess = false/unset) -> Should Fail (404 Not Found)
// API returns 404 for private repos you can't access, not 403, to avoid leaking existence.
testCtx.ExpectedCode = http.StatusNotFound
t.Run("Cross-Repo Access Denied (Default)", doAPIGetRepository(testCtx, nil))
// Case A: Default (AllowCrossRepoAccess = true by default now) -> Should Succeed (200) Read-Only
// API returns 404 if denied (hidden), 200 if allowed.
testCtx.ExpectedCode = http.StatusOK
t.Run("Cross-Repo Access Allowed (Default)", doAPIGetRepository(testCtx, func(t *testing.T, r structs.Repository) {
assert.Equal(t, "repo-B", r.Name)
}))
// Case B: Enable AllowCrossRepoAccess
// Case B: Explicitly Disable AllowCrossRepoAccess
org, err := org_model.GetOrgByName(t.Context(), orgName)
require.NoError(t, err)
cfg := &repo_model.ActionsConfig{
AllowCrossRepoAccess: true,
AllowCrossRepoAccess: false,
}
err = actions_model.SetOrgActionsConfig(t.Context(), org.ID, cfg)
require.NoError(t, err)
// Retry -> Should Succeed (200) - Read Only
testCtx.ExpectedCode = http.StatusOK
t.Run("Cross-Repo Access Allowed", doAPIGetRepository(testCtx, func(t *testing.T, r structs.Repository) {
assert.Equal(t, "repo-B", r.Name)
}))
// Retry -> Should Fail (404 Not Found)
testCtx.ExpectedCode = http.StatusNotFound
t.Run("Cross-Repo Access Denied (Disabled)", doAPIGetRepository(testCtx, nil))
// 6. Test Cross-Repo Package Access
t.Run("Cross-Repo Package Access", func(t *testing.T) {