mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-22 10:43:40 +02:00
fixes
This commit is contained in:
parent
feb791cccf
commit
7cccb84fdb
@ -346,13 +346,37 @@ func GetActionsUserRepoPermission(ctx context.Context, repo *repo_model.Reposito
|
|||||||
effectivePerms, err = repo_model.UnmarshalTokenPermissions(task.Job.TokenPermissions)
|
effectivePerms, err = repo_model.UnmarshalTokenPermissions(task.Job.TokenPermissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Fall back to repository settings if unmarshal fails
|
// Fall back to repository settings if unmarshal fails
|
||||||
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
// If following org config, we need to load it
|
||||||
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
|
||||||
|
} else {
|
||||||
|
effectivePerms = orgCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||||
|
effectivePerms = orgCfg.ClampPermissions(effectivePerms)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
||||||
|
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No workflow permissions or job not found, use repository settings
|
// No workflow permissions or job not found, use repository settings
|
||||||
effectivePerms = actionsCfg.GetEffectiveTokenPermissions(task.IsForkPullRequest)
|
if !actionsCfg.OverrideOrgConfig && repo.Owner.IsOrganization() {
|
||||||
effectivePerms = actionsCfg.ClampPermissions(effectivePerms)
|
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
|
// Set up per-unit access modes based on configured permissions
|
||||||
|
|||||||
@ -310,8 +310,8 @@ type ActionsConfig struct {
|
|||||||
AllowCrossRepoAccess bool `json:"allow_cross_repo_access,omitempty"`
|
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 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"`
|
||||||
// FollowOrgConfig indicates if this repository should follow the organization-level configuration
|
// OverrideOrgConfig indicates if this repository should override the organization-level configuration
|
||||||
FollowOrgConfig bool `json:"follow_org_config,omitempty"`
|
OverrideOrgConfig bool `json:"override_org_config,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
func (cfg *ActionsConfig) EnableWorkflow(file string) {
|
||||||
|
|||||||
@ -46,7 +46,7 @@ func ActionsGeneralSettings(ctx *context.Context) {
|
|||||||
|
|
||||||
// Follow org config (only for repos in orgs)
|
// Follow org config (only for repos in orgs)
|
||||||
ctx.Data["IsInOrg"] = ctx.Repo.Repository.Owner.IsOrganization()
|
ctx.Data["IsInOrg"] = ctx.Repo.Repository.Owner.IsOrganization()
|
||||||
ctx.Data["FollowOrgConfig"] = actionsCfg.FollowOrgConfig
|
ctx.Data["OverrideOrgConfig"] = actionsCfg.OverrideOrgConfig
|
||||||
|
|
||||||
if ctx.Repo.Repository.IsPrivate {
|
if ctx.Repo.Repository.IsPrivate {
|
||||||
collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
|
collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
|
||||||
@ -146,11 +146,12 @@ func UpdateTokenPermissions(ctx *context.Context) {
|
|||||||
|
|
||||||
actionsCfg := actionsUnit.ActionsConfig()
|
actionsCfg := actionsUnit.ActionsConfig()
|
||||||
|
|
||||||
// Update Follow Org Config (for repos in orgs)
|
// Update Override Org Config (for repos in orgs)
|
||||||
actionsCfg.FollowOrgConfig = ctx.FormBool("follow_org_config")
|
// 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)
|
// Update permission mode (only if overriding org config)
|
||||||
if !actionsCfg.FollowOrgConfig {
|
if actionsCfg.OverrideOrgConfig {
|
||||||
permissionMode := repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode"))
|
permissionMode := repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode"))
|
||||||
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted ||
|
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted ||
|
||||||
permissionMode == repo_model.ActionsTokenPermissionModePermissive ||
|
permissionMode == repo_model.ActionsTokenPermissionModePermissive ||
|
||||||
@ -164,7 +165,7 @@ func UpdateTokenPermissions(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update Maximum Permissions (radio buttons: none/read/write)
|
// 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 {
|
parseMaxPerm := func(name string) perm.AccessMode {
|
||||||
value := ctx.FormString("max_" + name)
|
value := ctx.FormString("max_" + name)
|
||||||
switch value {
|
switch value {
|
||||||
|
|||||||
@ -33,13 +33,13 @@
|
|||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
|
||||||
{{if .IsInOrg}}
|
{{if .IsInOrg}}
|
||||||
<!-- Follow Organization Configuration -->
|
<!-- Override Organization Configuration -->
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="follow_org_config" id="follow-org-config" {{if .FollowOrgConfig}}checked{{end}} class="js-follow-org-config">
|
<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.follow_org"}}</strong></label>
|
<label><strong>{{ctx.Locale.Tr "actions.general.token_permissions.override_org"}}</strong></label>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|||||||
@ -177,7 +177,7 @@ func testActionsTokenPermissionsMode(u *url.URL, mode string, expectReadOnly boo
|
|||||||
require.NoError(t, err, "Actions unit should exist for repo4")
|
require.NoError(t, err, "Actions unit should exist for repo4")
|
||||||
actionsCfg := actionsUnit.ActionsConfig()
|
actionsCfg := actionsUnit.ActionsConfig()
|
||||||
actionsCfg.TokenPermissionMode = repo_model.ActionsTokenPermissionMode(mode)
|
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
|
// Update the config
|
||||||
actionsUnit.Config = actionsCfg
|
actionsUnit.Config = actionsCfg
|
||||||
require.NoError(t, repo_model.UpdateRepoUnit(t.Context(), actionsUnit))
|
require.NoError(t, repo_model.UpdateRepoUnit(t.Context(), actionsUnit))
|
||||||
@ -343,11 +343,12 @@ func TestActionsTokenPackagePermission(t *testing.T) {
|
|||||||
runner := newMockRunner()
|
runner := newMockRunner()
|
||||||
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
|
runner.registerAsRepoRunner(t, repo.OwnerName, repo.Name, "mock-runner", []string{"ubuntu-latest"}, false)
|
||||||
|
|
||||||
// Set Config: Custom Mode, Max Packages = Write
|
// Set Config: Custom Mode, Max Packages = Write, Max Code = Read
|
||||||
// This should implied Default Packages = Write (because Custom defaults to Max)
|
|
||||||
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/actions/general/token_permissions", repo.OwnerName, repo.Name), map[string]string{
|
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",
|
"token_permission_mode": "custom",
|
||||||
"max_packages": "write",
|
"max_packages": "write",
|
||||||
|
"max_code": "read", // Ensure repo read access if needed
|
||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
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)
|
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})).
|
uploadReq := NewRequestWithBody(t, "PUT", writePackageURL, bytes.NewReader([]byte{1, 2, 3})).
|
||||||
AddTokenAuth(taskToken)
|
AddTokenAuth(taskToken)
|
||||||
|
|
||||||
// Should Succeed (201)
|
// Should Succeed (201)
|
||||||
MakeRequest(t, uploadReq, http.StatusCreated)
|
MakeRequest(t, uploadReq, http.StatusCreated)
|
||||||
})
|
})
|
||||||
@ -441,26 +442,26 @@ func TestActionsCrossRepoAccess(t *testing.T) {
|
|||||||
Reponame: "repo-B",
|
Reponame: "repo-B",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case A: Default (AllowCrossRepoAccess = false/unset) -> Should Fail (404 Not Found)
|
// Case A: Default (AllowCrossRepoAccess = true by default now) -> Should Succeed (200) Read-Only
|
||||||
// API returns 404 for private repos you can't access, not 403, to avoid leaking existence.
|
// API returns 404 if denied (hidden), 200 if allowed.
|
||||||
testCtx.ExpectedCode = http.StatusNotFound
|
testCtx.ExpectedCode = http.StatusOK
|
||||||
t.Run("Cross-Repo Access Denied (Default)", doAPIGetRepository(testCtx, nil))
|
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)
|
org, err := org_model.GetOrgByName(t.Context(), orgName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cfg := &repo_model.ActionsConfig{
|
cfg := &repo_model.ActionsConfig{
|
||||||
AllowCrossRepoAccess: true,
|
AllowCrossRepoAccess: false,
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
|
||||||
// Retry -> Should Succeed (200) - Read Only
|
// Retry -> Should Fail (404 Not Found)
|
||||||
testCtx.ExpectedCode = http.StatusOK
|
testCtx.ExpectedCode = http.StatusNotFound
|
||||||
t.Run("Cross-Repo Access Allowed", doAPIGetRepository(testCtx, func(t *testing.T, r structs.Repository) {
|
t.Run("Cross-Repo Access Denied (Disabled)", doAPIGetRepository(testCtx, nil))
|
||||||
assert.Equal(t, "repo-B", r.Name)
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 6. Test Cross-Repo Package Access
|
// 6. Test Cross-Repo Package Access
|
||||||
t.Run("Cross-Repo Package Access", func(t *testing.T) {
|
t.Run("Cross-Repo Package Access", func(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user