0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-02-14 21:15:43 +01:00
This commit is contained in:
Excellencedev 2025-12-18 12:00:47 +01:00
parent 43e96d5ead
commit 2a204e36a7
6 changed files with 338 additions and 2 deletions

View File

@ -176,6 +176,8 @@ const (
ActionsTokenPermissionModePermissive ActionsTokenPermissionMode = "permissive"
// ActionsTokenPermissionModeRestricted - read access by default
ActionsTokenPermissionModeRestricted ActionsTokenPermissionMode = "restricted"
// ActionsTokenPermissionModeCustom - user-defined permissions
ActionsTokenPermissionModeCustom ActionsTokenPermissionMode = "custom"
)
// ActionsTokenPermissions defines the permissions for different repository units
@ -194,6 +196,46 @@ type ActionsTokenPermissions struct {
Wiki perm.AccessMode `json:"wiki"`
}
// HasRead checks if the permission has read access for the given scope
func (p ActionsTokenPermissions) HasRead(scope string) bool {
var mode perm.AccessMode
switch scope {
case "actions":
mode = p.Actions
case "contents":
mode = p.Contents
case "issues":
mode = p.Issues
case "packages":
mode = p.Packages
case "pull_requests":
mode = p.PullRequests
case "wiki":
mode = p.Wiki
}
return mode >= perm.AccessModeRead
}
// HasWrite checks if the permission has write access for the given scope
func (p ActionsTokenPermissions) HasWrite(scope string) bool {
var mode perm.AccessMode
switch scope {
case "actions":
mode = p.Actions
case "contents":
mode = p.Contents
case "issues":
mode = p.Issues
case "packages":
mode = p.Packages
case "pull_requests":
mode = p.PullRequests
case "wiki":
mode = p.Wiki
}
return mode >= perm.AccessModeWrite
}
// DefaultActionsTokenPermissions returns the default permissions for permissive mode
func DefaultActionsTokenPermissions(mode ActionsTokenPermissionMode) ActionsTokenPermissions {
if mode == ActionsTokenPermissionModeRestricted {

View File

@ -3957,6 +3957,9 @@ general.token_permissions.access_none = No access
general.token_permissions.update_success = Token permissions updated successfully.
general.token_permissions.cross_repo = Cross-Repository Access
general.token_permissions.cross_repo_desc = Allow workflows in this organization to access other repositories within the same organization.
general.token_permissions.custom = Custom permissions
general.token_permissions.custom.description = Configure permissions for each scope individually.
general.token_permissions.individual = Individual Permissions
[projects]
deleted.display_name = Deleted Project

View File

@ -7,6 +7,7 @@ import (
"net/http"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/services/context"
@ -32,6 +33,8 @@ func ActionsGeneral(ctx *context.Context) {
ctx.Data["TokenPermissionMode"] = actionsCfg.GetTokenPermissionMode()
ctx.Data["TokenPermissionModePermissive"] = repo_model.ActionsTokenPermissionModePermissive
ctx.Data["TokenPermissionModeRestricted"] = repo_model.ActionsTokenPermissionModeRestricted
ctx.Data["TokenPermissionModeCustom"] = repo_model.ActionsTokenPermissionModeCustom
ctx.Data["DefaultTokenPermissions"] = actionsCfg.GetEffectiveTokenPermissions(false)
ctx.Data["AllowCrossRepoAccess"] = actionsCfg.AllowCrossRepoAccess
@ -52,10 +55,35 @@ func ActionsGeneralPost(ctx *context.Context) {
// Update Token Permission Mode
permissionMode := repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode"))
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted || permissionMode == repo_model.ActionsTokenPermissionModePermissive {
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted ||
permissionMode == repo_model.ActionsTokenPermissionModePermissive ||
permissionMode == repo_model.ActionsTokenPermissionModeCustom {
actionsCfg.TokenPermissionMode = permissionMode
}
if actionsCfg.TokenPermissionMode == repo_model.ActionsTokenPermissionModeCustom {
parsePerm := func(name string) perm.AccessMode {
if ctx.FormBool(name + "_write") {
return perm.AccessModeWrite
}
if ctx.FormBool(name + "_read") {
return perm.AccessModeRead
}
return perm.AccessModeNone
}
actionsCfg.DefaultTokenPermissions = &repo_model.ActionsTokenPermissions{
Actions: parsePerm("actions"),
Contents: parsePerm("contents"),
Issues: parsePerm("issues"),
Packages: parsePerm("packages"),
PullRequests: parsePerm("pull_requests"),
Wiki: parsePerm("wiki"),
}
} else {
actionsCfg.DefaultTokenPermissions = nil
}
// Update Cross-Repo Access
actionsCfg.AllowCrossRepoAccess = ctx.FormBool("allow_cross_repo_access")

View File

@ -8,6 +8,7 @@ import (
"net/http"
"strings"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
@ -40,6 +41,8 @@ func ActionsGeneralSettings(ctx *context.Context) {
ctx.Data["TokenPermissionMode"] = actionsCfg.GetTokenPermissionMode()
ctx.Data["TokenPermissionModePermissive"] = repo_model.ActionsTokenPermissionModePermissive
ctx.Data["TokenPermissionModeRestricted"] = repo_model.ActionsTokenPermissionModeRestricted
ctx.Data["TokenPermissionModeCustom"] = repo_model.ActionsTokenPermissionModeCustom
ctx.Data["DefaultTokenPermissions"] = actionsCfg.GetEffectiveTokenPermissions(false)
if ctx.Repo.Repository.IsPrivate {
collaborativeOwnerIDs := actionsCfg.CollaborativeOwnerIDs
@ -141,7 +144,9 @@ func UpdateTokenPermissions(ctx *context.Context) {
// Update permission mode
permissionMode := repo_model.ActionsTokenPermissionMode(ctx.FormString("token_permission_mode"))
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted || permissionMode == repo_model.ActionsTokenPermissionModePermissive {
if permissionMode == repo_model.ActionsTokenPermissionModeRestricted ||
permissionMode == repo_model.ActionsTokenPermissionModePermissive ||
permissionMode == repo_model.ActionsTokenPermissionModeCustom {
actionsCfg.TokenPermissionMode = permissionMode
} else {
ctx.Flash.Error("Invalid token permission mode")
@ -149,6 +154,29 @@ func UpdateTokenPermissions(ctx *context.Context) {
return
}
if actionsCfg.TokenPermissionMode == repo_model.ActionsTokenPermissionModeCustom {
parsePerm := func(name string) perm.AccessMode {
if ctx.FormBool(name + "_write") {
return perm.AccessModeWrite
}
if ctx.FormBool(name + "_read") {
return perm.AccessModeRead
}
return perm.AccessModeNone
}
actionsCfg.DefaultTokenPermissions = &repo_model.ActionsTokenPermissions{
Actions: parsePerm("actions"),
Contents: parsePerm("contents"),
Issues: parsePerm("issues"),
Packages: parsePerm("packages"),
PullRequests: parsePerm("pull_requests"),
Wiki: parsePerm("wiki"),
}
} else {
actionsCfg.DefaultTokenPermissions = nil
}
if err := repo_model.UpdateRepoUnit(ctx, actionsUnit); err != nil {
ctx.ServerError("UpdateRepoUnit", err)
return

View File

@ -25,6 +25,103 @@
<p class="help">{{.locale.Tr "actions.general.token_permissions.restricted.description"}}</p>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="token_permission_mode" value="custom" {{if eq .TokenPermissionMode .TokenPermissionModeCustom}}checked{{end}}>
<label>{{.locale.Tr "actions.general.token_permissions.custom"}}</label>
<p class="help">{{.locale.Tr "actions.general.token_permissions.custom.description"}}</p>
</div>
</div>
</div>
</div>
<div id="custom-permissions" class="ui segment" style="display: none;">
<h5 class="ui header">{{.locale.Tr "actions.general.token_permissions.individual"}}</h5>
<div class="ui grid">
<!-- Actions -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "actions.actions"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="actions_read" {{if call $.DefaultTokenPermissions.HasRead "actions"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="actions_write" {{if call $.DefaultTokenPermissions.HasWrite "actions"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Contents -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "general.token_permissions.contents"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="contents_read" {{if call $.DefaultTokenPermissions.HasRead "contents"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="contents_write" {{if call $.DefaultTokenPermissions.HasWrite "contents"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Issues -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "general.token_permissions.issues"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="issues_read" {{if call $.DefaultTokenPermissions.HasRead "issues"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="issues_write" {{if call $.DefaultTokenPermissions.HasWrite "issues"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Packages -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "general.token_permissions.packages"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="packages_read" {{if call $.DefaultTokenPermissions.HasRead "packages"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="packages_write" {{if call $.DefaultTokenPermissions.HasWrite "packages"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Pull Requests -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "general.token_permissions.pull_requests"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="pull_requests_read" {{if call $.DefaultTokenPermissions.HasRead "pull_requests"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="pull_requests_write" {{if call $.DefaultTokenPermissions.HasWrite "pull_requests"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Wiki -->
<div class="eight wide column">
<div class="field">
<label>{{.locale.Tr "general.token_permissions.wiki"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="wiki_read" {{if call $.DefaultTokenPermissions.HasRead "wiki"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="wiki_write" {{if call $.DefaultTokenPermissions.HasWrite "wiki"}}checked{{end}}>
<label>{{.locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
</div>
</div>
@ -45,3 +142,22 @@
</div>
</div>
{{template "org/settings/layout_footer" .}}
<script>
window.addEventListener('load', function() {
const customPerms = document.getElementById('custom-permissions');
const radios = document.querySelectorAll('input[name="token_permission_mode"]');
function toggleCustom() {
const selected = document.querySelector('input[name="token_permission_mode"]:checked');
if (selected && selected.value === 'custom') {
customPerms.style.display = 'block';
} else {
customPerms.style.display = 'none';
}
}
radios.forEach(r => r.addEventListener('change', toggleCustom));
toggleCustom();
});
</script>

View File

@ -94,7 +94,107 @@
</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="token_permission_mode" value="custom" {{if eq .TokenPermissionMode .TokenPermissionModeCustom}}checked{{end}}>
<label>
<strong>{{ctx.Locale.Tr "actions.general.token_permissions.custom"}}</strong>
<p class="tw-text-secondary tw-m-0">{{ctx.Locale.Tr "actions.general.token_permissions.custom.description"}}</p>
</label>
</div>
</div>
</div>
<div id="custom-permissions" class="ui segment" style="display: none;">
<h5 class="ui header">{{ctx.Locale.Tr "actions.general.token_permissions.individual"}}</h5>
<div class="ui grid">
<!-- Actions -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "actions.actions"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="actions_read" {{if call $.DefaultTokenPermissions.HasRead "actions"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="actions_write" {{if call $.DefaultTokenPermissions.HasWrite "actions"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Contents -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "general.token_permissions.contents"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="contents_read" {{if call $.DefaultTokenPermissions.HasRead "contents"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="contents_write" {{if call $.DefaultTokenPermissions.HasWrite "contents"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Issues -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "general.token_permissions.issues"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="issues_read" {{if call $.DefaultTokenPermissions.HasRead "issues"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="issues_write" {{if call $.DefaultTokenPermissions.HasWrite "issues"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Packages -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "general.token_permissions.packages"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="packages_read" {{if call $.DefaultTokenPermissions.HasRead "packages"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="packages_write" {{if call $.DefaultTokenPermissions.HasWrite "packages"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Pull Requests -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "general.token_permissions.pull_requests"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="pull_requests_read" {{if call $.DefaultTokenPermissions.HasRead "pull_requests"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="pull_requests_write" {{if call $.DefaultTokenPermissions.HasWrite "pull_requests"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
<!-- Wiki -->
<div class="eight wide column">
<div class="field">
<label>{{ctx.Locale.Tr "general.token_permissions.wiki"}}</label>
<div class="ui checkbox">
<input type="checkbox" name="wiki_read" {{if call $.DefaultTokenPermissions.HasRead "wiki"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_read"}}</label>
</div>
<div class="ui checkbox">
<input type="checkbox" name="wiki_write" {{if call $.DefaultTokenPermissions.HasWrite "wiki"}}checked{{end}}>
<label>{{ctx.Locale.Tr "general.token_permissions.access_write"}}</label>
</div>
</div>
</div>
</div>
</div>
<div class="ui info message">
<p>{{ctx.Locale.Tr "actions.general.token_permissions.fork_pr_note"}}</p>
</div>
@ -106,3 +206,22 @@
</div>
{{end}}
</div>
<script>
window.addEventListener('load', function() {
const customPerms = document.getElementById('custom-permissions');
const radios = document.querySelectorAll('input[name="token_permission_mode"]');
function toggleCustom() {
const selected = document.querySelector('input[name="token_permission_mode"]:checked');
if (selected && selected.value === 'custom') {
customPerms.style.display = 'block';
} else {
customPerms.style.display = 'none';
}
}
radios.forEach(r => r.addEventListener('change', toggleCustom));
toggleCustom();
});
</script>