diff --git a/models/actions/config.go b/models/actions/config.go index 45d248af65..c0bb3a0b74 100644 --- a/models/actions/config.go +++ b/models/actions/config.go @@ -22,7 +22,7 @@ func GetOrgActionsConfig(ctx context.Context, orgID int64) (*repo_model.ActionsC cfg := &repo_model.ActionsConfig{} if val == "" { // Return defaults if no config exists - cfg.AllowCrossRepoAccess = true + cfg.CrossRepoMode = repo_model.ActionsCrossRepoModeAll return cfg, nil } diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index da41878dc8..4b985fcbe5 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -305,8 +305,8 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito if err != nil { return perm, err } - if !orgCfg.AllowCrossRepoAccess { - // Deny access if cross-repo is disabled in Org + if !orgCfg.IsRepoAllowedCrossAccess(repo.ID) { + // Deny access if cross-repo is disabled or not allowed for this specific repo return perm, nil } } diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index cba643cd4f..86a16a3804 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -180,6 +180,18 @@ const ( ActionsTokenPermissionModeCustom ActionsTokenPermissionMode = "custom" ) +// ActionsCrossRepoMode defines the mode for cross-repository access +type ActionsCrossRepoMode string + +const ( + // ActionsCrossRepoModeNone - no cross-repository access allowed + ActionsCrossRepoModeNone ActionsCrossRepoMode = "none" + // ActionsCrossRepoModeAll - access allowed to all repositories in the organization + ActionsCrossRepoModeAll ActionsCrossRepoMode = "all" + // ActionsCrossRepoModeSelected - access allowed only to selected repositories + ActionsCrossRepoModeSelected ActionsCrossRepoMode = "selected" +) + // ActionsTokenPermissions defines the permissions for different repository units type ActionsTokenPermissions struct { // Code (repository code) - read/write/none @@ -306,8 +318,8 @@ type ActionsConfig struct { // MaxTokenPermissions defines the absolute maximum permissions any token can have in this context. // Workflow YAML "permissions" keywords can reduce permissions but never exceed this ceiling. MaxTokenPermissions *ActionsTokenPermissions `json:"max_token_permissions,omitempty"` - // AllowCrossRepoAccess indicates if actions in this repo/org can access other repos in the same org - AllowCrossRepoAccess bool `json:"allow_cross_repo_access,omitempty"` + // CrossRepoMode indicates which repos in the org can be accessed (none, all, or selected) + CrossRepoMode ActionsCrossRepoMode `json:"cross_repo_mode,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"` // OverrideOrgConfig indicates if this repository should override the organization-level configuration @@ -405,17 +417,15 @@ func (cfg *ActionsConfig) ClampPermissions(perms ActionsTokenPermissions) Action } // IsRepoAllowedCrossAccess checks if a specific repo is allowed for cross-repo access -// Returns true if AllowCrossRepoAccess is enabled AND (AllowedCrossRepoIDs is empty OR repoID is in the list) func (cfg *ActionsConfig) IsRepoAllowedCrossAccess(repoID int64) bool { - if !cfg.AllowCrossRepoAccess { + switch cfg.CrossRepoMode { + case ActionsCrossRepoModeAll: + return true + case ActionsCrossRepoModeSelected: + return slices.Contains(cfg.AllowedCrossRepoIDs, repoID) + default: // ActionsCrossRepoModeNone or invalid return false } - // If no specific repos are configured, allow all - if len(cfg.AllowedCrossRepoIDs) == 0 { - return true - } - // Check if repo is in the allowed list - return slices.Contains(cfg.AllowedCrossRepoIDs, repoID) } // FromDB fills up a ActionsConfig from serialized format. diff --git a/routers/web/org/setting/actions.go b/routers/web/org/setting/actions.go index a0b7c0d4ab..3c37c41aa4 100644 --- a/routers/web/org/setting/actions.go +++ b/routers/web/org/setting/actions.go @@ -37,8 +37,10 @@ func ActionsGeneralSettings(ctx *context.Context) { ctx.Data["TokenPermissionModeCustom"] = repo_model.ActionsTokenPermissionModeCustom ctx.Data["MaxTokenPermissions"] = actionsCfg.GetMaxTokenPermissions() - ctx.Data["AllowCrossRepoAccess"] = actionsCfg.AllowCrossRepoAccess - ctx.Data["HasSelectedRepos"] = len(actionsCfg.AllowedCrossRepoIDs) > 0 + ctx.Data["CrossRepoMode"] = actionsCfg.CrossRepoMode + ctx.Data["ActionsCrossRepoModeNone"] = repo_model.ActionsCrossRepoModeNone + ctx.Data["ActionsCrossRepoModeAll"] = repo_model.ActionsCrossRepoModeAll + ctx.Data["ActionsCrossRepoModeSelected"] = repo_model.ActionsCrossRepoModeSelected // Load Allowed Repositories var allowedRepos []*repo_model.Repository @@ -116,14 +118,17 @@ func UpdateTokenPermissions(ctx *context.Context) { crossRepoMode := ctx.FormString("cross_repo_mode") switch crossRepoMode { case "none": - actionsCfg.AllowCrossRepoAccess = false + actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeNone actionsCfg.AllowedCrossRepoIDs = nil case "all": - actionsCfg.AllowCrossRepoAccess = true + actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeAll actionsCfg.AllowedCrossRepoIDs = nil case "selected": - actionsCfg.AllowCrossRepoAccess = true + actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeSelected // Keep existing AllowedCrossRepoIDs, will be updated by separate API + default: + // Default to none if invalid + actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeNone } if err := actions_model.SetOrgActionsConfig(ctx, ctx.Org.Organization.AsUser().ID, actionsCfg); err != nil { diff --git a/templates/org/settings/actions_general.tmpl b/templates/org/settings/actions_general.tmpl index 3c1692a30f..e65c83d381 100644 --- a/templates/org/settings/actions_general.tmpl +++ b/templates/org/settings/actions_general.tmpl @@ -16,19 +16,19 @@
- +
- +
- +
diff --git a/tests/integration/actions_job_token_test.go b/tests/integration/actions_job_token_test.go index dc52955497..1d8ce58bc4 100644 --- a/tests/integration/actions_job_token_test.go +++ b/tests/integration/actions_job_token_test.go @@ -455,7 +455,7 @@ func TestActionsCrossRepoAccess(t *testing.T) { require.NoError(t, err) cfg := &repo_model.ActionsConfig{ - AllowCrossRepoAccess: false, + CrossRepoMode: repo_model.ActionsCrossRepoModeNone, } err = actions_model.SetOrgActionsConfig(t.Context(), org.ID, cfg) require.NoError(t, err) @@ -484,7 +484,7 @@ func TestActionsCrossRepoAccess(t *testing.T) { // By default, cross-repo is disabled // Explicitly set it to false to ensure test determinism (in case defaults change) require.NoError(t, actions_model.SetOrgActionsConfig(t.Context(), org.ID, &repo_model.ActionsConfig{ - AllowCrossRepoAccess: false, + CrossRepoMode: repo_model.ActionsCrossRepoModeNone, })) // Try to download with cross-repo disabled - should fail @@ -494,7 +494,7 @@ func TestActionsCrossRepoAccess(t *testing.T) { // Enable cross-repo access require.NoError(t, actions_model.SetOrgActionsConfig(t.Context(), org.ID, &repo_model.ActionsConfig{ - AllowCrossRepoAccess: true, + CrossRepoMode: repo_model.ActionsCrossRepoModeAll, })) // Try to download with cross-repo enabled - should succeed