0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-02-16 12:15:18 +01:00

CrossRepoMode

This commit is contained in:
Excellencedev 2026-01-17 15:57:50 +01:00
parent c770087902
commit 2480c30847
6 changed files with 39 additions and 24 deletions

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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.

View File

@ -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 {

View File

@ -16,19 +16,19 @@
<div class="grouped fields">
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="cross_repo_mode" value="none" {{if not .AllowCrossRepoAccess}}checked{{end}} class="js-cross-repo-mode">
<input type="radio" name="cross_repo_mode" value="none" {{if eq .CrossRepoMode .ActionsCrossRepoModeNone}}checked{{end}} class="js-cross-repo-mode">
<label>{{ctx.Locale.Tr "settings.disabled"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="cross_repo_mode" value="all" {{if and .AllowCrossRepoAccess (not .HasSelectedRepos)}}checked{{end}} class="js-cross-repo-mode">
<input type="radio" name="cross_repo_mode" value="all" {{if eq .CrossRepoMode .ActionsCrossRepoModeAll}}checked{{end}} class="js-cross-repo-mode">
<label>{{ctx.Locale.Tr "actions.general.token_permissions.cross_repo_all"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="cross_repo_mode" value="selected" {{if and .AllowCrossRepoAccess .HasSelectedRepos}}checked{{end}} class="js-cross-repo-mode">
<input type="radio" name="cross_repo_mode" value="selected" {{if eq .CrossRepoMode .ActionsCrossRepoModeSelected}}checked{{end}} class="js-cross-repo-mode">
<label>{{ctx.Locale.Tr "actions.general.token_permissions.cross_repo_selected"}}</label>
</div>
</div>

View File

@ -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