mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-12 04:43:32 +02:00
CrossRepoMode
This commit is contained in:
parent
c770087902
commit
2480c30847
@ -22,7 +22,7 @@ func GetOrgActionsConfig(ctx context.Context, orgID int64) (*repo_model.ActionsC
|
|||||||
cfg := &repo_model.ActionsConfig{}
|
cfg := &repo_model.ActionsConfig{}
|
||||||
if val == "" {
|
if val == "" {
|
||||||
// Return defaults if no config exists
|
// Return defaults if no config exists
|
||||||
cfg.AllowCrossRepoAccess = true
|
cfg.CrossRepoMode = repo_model.ActionsCrossRepoModeAll
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -305,8 +305,8 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return perm, err
|
return perm, err
|
||||||
}
|
}
|
||||||
if !orgCfg.AllowCrossRepoAccess {
|
if !orgCfg.IsRepoAllowedCrossAccess(repo.ID) {
|
||||||
// Deny access if cross-repo is disabled in Org
|
// Deny access if cross-repo is disabled or not allowed for this specific repo
|
||||||
return perm, nil
|
return perm, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,6 +180,18 @@ const (
|
|||||||
ActionsTokenPermissionModeCustom ActionsTokenPermissionMode = "custom"
|
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
|
// ActionsTokenPermissions defines the permissions for different repository units
|
||||||
type ActionsTokenPermissions struct {
|
type ActionsTokenPermissions struct {
|
||||||
// Code (repository code) - read/write/none
|
// 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.
|
// 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.
|
// Workflow YAML "permissions" keywords can reduce permissions but never exceed this ceiling.
|
||||||
MaxTokenPermissions *ActionsTokenPermissions `json:"max_token_permissions,omitempty"`
|
MaxTokenPermissions *ActionsTokenPermissions `json:"max_token_permissions,omitempty"`
|
||||||
// AllowCrossRepoAccess indicates if actions in this repo/org can access other repos in the same org
|
// CrossRepoMode indicates which repos in the org can be accessed (none, all, or selected)
|
||||||
AllowCrossRepoAccess bool `json:"allow_cross_repo_access,omitempty"`
|
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 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"`
|
AllowedCrossRepoIDs []int64 `json:"allowed_cross_repo_ids,omitempty"`
|
||||||
// OverrideOrgConfig indicates if this repository should override the organization-level configuration
|
// 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
|
// 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 {
|
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
|
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.
|
// FromDB fills up a ActionsConfig from serialized format.
|
||||||
|
|||||||
@ -37,8 +37,10 @@ func ActionsGeneralSettings(ctx *context.Context) {
|
|||||||
ctx.Data["TokenPermissionModeCustom"] = repo_model.ActionsTokenPermissionModeCustom
|
ctx.Data["TokenPermissionModeCustom"] = repo_model.ActionsTokenPermissionModeCustom
|
||||||
ctx.Data["MaxTokenPermissions"] = actionsCfg.GetMaxTokenPermissions()
|
ctx.Data["MaxTokenPermissions"] = actionsCfg.GetMaxTokenPermissions()
|
||||||
|
|
||||||
ctx.Data["AllowCrossRepoAccess"] = actionsCfg.AllowCrossRepoAccess
|
ctx.Data["CrossRepoMode"] = actionsCfg.CrossRepoMode
|
||||||
ctx.Data["HasSelectedRepos"] = len(actionsCfg.AllowedCrossRepoIDs) > 0
|
ctx.Data["ActionsCrossRepoModeNone"] = repo_model.ActionsCrossRepoModeNone
|
||||||
|
ctx.Data["ActionsCrossRepoModeAll"] = repo_model.ActionsCrossRepoModeAll
|
||||||
|
ctx.Data["ActionsCrossRepoModeSelected"] = repo_model.ActionsCrossRepoModeSelected
|
||||||
|
|
||||||
// Load Allowed Repositories
|
// Load Allowed Repositories
|
||||||
var allowedRepos []*repo_model.Repository
|
var allowedRepos []*repo_model.Repository
|
||||||
@ -116,14 +118,17 @@ func UpdateTokenPermissions(ctx *context.Context) {
|
|||||||
crossRepoMode := ctx.FormString("cross_repo_mode")
|
crossRepoMode := ctx.FormString("cross_repo_mode")
|
||||||
switch crossRepoMode {
|
switch crossRepoMode {
|
||||||
case "none":
|
case "none":
|
||||||
actionsCfg.AllowCrossRepoAccess = false
|
actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeNone
|
||||||
actionsCfg.AllowedCrossRepoIDs = nil
|
actionsCfg.AllowedCrossRepoIDs = nil
|
||||||
case "all":
|
case "all":
|
||||||
actionsCfg.AllowCrossRepoAccess = true
|
actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeAll
|
||||||
actionsCfg.AllowedCrossRepoIDs = nil
|
actionsCfg.AllowedCrossRepoIDs = nil
|
||||||
case "selected":
|
case "selected":
|
||||||
actionsCfg.AllowCrossRepoAccess = true
|
actionsCfg.CrossRepoMode = repo_model.ActionsCrossRepoModeSelected
|
||||||
// Keep existing AllowedCrossRepoIDs, will be updated by separate API
|
// 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 {
|
if err := actions_model.SetOrgActionsConfig(ctx, ctx.Org.Organization.AsUser().ID, actionsCfg); err != nil {
|
||||||
|
|||||||
@ -16,19 +16,19 @@
|
|||||||
<div class="grouped fields">
|
<div class="grouped fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio checkbox">
|
<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>
|
<label>{{ctx.Locale.Tr "settings.disabled"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio checkbox">
|
<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>
|
<label>{{ctx.Locale.Tr "actions.general.token_permissions.cross_repo_all"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio checkbox">
|
<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>
|
<label>{{ctx.Locale.Tr "actions.general.token_permissions.cross_repo_selected"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -455,7 +455,7 @@ func TestActionsCrossRepoAccess(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cfg := &repo_model.ActionsConfig{
|
cfg := &repo_model.ActionsConfig{
|
||||||
AllowCrossRepoAccess: false,
|
CrossRepoMode: repo_model.ActionsCrossRepoModeNone,
|
||||||
}
|
}
|
||||||
err = actions_model.SetOrgActionsConfig(t.Context(), org.ID, cfg)
|
err = actions_model.SetOrgActionsConfig(t.Context(), org.ID, cfg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -484,7 +484,7 @@ func TestActionsCrossRepoAccess(t *testing.T) {
|
|||||||
// By default, cross-repo is disabled
|
// By default, cross-repo is disabled
|
||||||
// Explicitly set it to false to ensure test determinism (in case defaults change)
|
// 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{
|
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
|
// Try to download with cross-repo disabled - should fail
|
||||||
@ -494,7 +494,7 @@ func TestActionsCrossRepoAccess(t *testing.T) {
|
|||||||
|
|
||||||
// Enable cross-repo access
|
// Enable cross-repo access
|
||||||
require.NoError(t, actions_model.SetOrgActionsConfig(t.Context(), org.ID, &repo_model.ActionsConfig{
|
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
|
// Try to download with cross-repo enabled - should succeed
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user