mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-26 06:16:56 +09:00
Adds OpenID Connect workload identity to Gitea Actions: jobs declaring `id-token: write` can request short-lived tokens to authenticate to cloud providers and other services without stored secrets. Tokens follow GitHub's format, so clients like `@actions/core` and existing trust policies carry over. - Issuer at `/api/actions/oidc` with discovery and JWKS, signed with a dedicated RS256 key - GitHub's claims, including `workflow_ref`, `job_workflow_ref` and the immutable `repo:owner@id/repo@id:ref:...` subject - `id-token: write` or `write-all` grant it, default permissions and fork pull requests never get it, owner and repository maximum permissions can disable it - Reusable workflows record the exact ref they were loaded from, and dispatch rejects refs that only resolve through a same-named tag - Generated signing keys are written atomically, and generated ES384 and ES512 OAuth2 keys use their own curves instead of P-256 - `github.ref_type` is `branch` for pull request runs, like on GitHub Fixes https://github.com/go-gitea/gitea/issues/26383 Fixes https://github.com/go-gitea/gitea/issues/33681 Docs: https://gitea.com/gitea/docs/pulls/526 Assisted-by: ChatGPT:gpt-5.6-sol, Claude Code:claude-opus-5-5 Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
109 lines
4.1 KiB
Go
109 lines
4.1 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dev/models/perm"
|
|
"gitea.dev/models/unit"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestActionsConfig(t *testing.T) {
|
|
cfg := &ActionsConfig{}
|
|
cfg.DisableWorkflow("test1.yaml")
|
|
assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
|
|
|
|
cfg.DisableWorkflow("test1.yaml")
|
|
assert.Equal(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
|
|
|
|
cfg.EnableWorkflow("test1.yaml")
|
|
assert.Equal(t, []string{}, cfg.DisabledWorkflows)
|
|
|
|
cfg.EnableWorkflow("test1.yaml")
|
|
assert.Equal(t, []string{}, cfg.DisabledWorkflows)
|
|
|
|
cfg.DisableWorkflow("test1.yaml")
|
|
cfg.DisableWorkflow("test2.yaml")
|
|
cfg.DisableWorkflow("test3.yaml")
|
|
assert.Equal(t, "test1.yaml,test2.yaml,test3.yaml", strings.Join(cfg.DisabledWorkflows, ","))
|
|
}
|
|
|
|
func TestActionsConfigTokenPermissions(t *testing.T) {
|
|
t.Run("Default Permission Mode", func(t *testing.T) {
|
|
cfg := &ActionsConfig{TokenPermissionMode: "invalid-value"}
|
|
_ = cfg.FromDB(nil)
|
|
assert.Equal(t, ActionsTokenPermissionModePermissive, cfg.TokenPermissionMode)
|
|
assert.Equal(t, perm.AccessModeWrite, cfg.GetDefaultTokenPermissions().UnitAccessModes[unit.TypeCode])
|
|
})
|
|
|
|
t.Run("Explicit Permission Mode", func(t *testing.T) {
|
|
cfg := &ActionsConfig{
|
|
TokenPermissionMode: ActionsTokenPermissionModeRestricted,
|
|
}
|
|
assert.Equal(t, ActionsTokenPermissionModeRestricted, cfg.TokenPermissionMode)
|
|
})
|
|
|
|
t.Run("Effective Permissions - Permissive Mode", func(t *testing.T) {
|
|
cfg := &ActionsConfig{
|
|
TokenPermissionMode: ActionsTokenPermissionModePermissive,
|
|
}
|
|
defaultPerms := cfg.GetDefaultTokenPermissions()
|
|
perms := cfg.ClampPermissions(defaultPerms)
|
|
assert.Equal(t, perm.AccessModeWrite, perms.UnitAccessModes[unit.TypeCode])
|
|
assert.Equal(t, perm.AccessModeWrite, perms.UnitAccessModes[unit.TypeIssues])
|
|
assert.Equal(t, perm.AccessModeWrite, perms.UnitAccessModes[unit.TypePackages])
|
|
assert.False(t, perms.IDToken)
|
|
assert.True(t, cfg.GetMaxTokenPermissions().IDToken)
|
|
})
|
|
|
|
t.Run("Effective Permissions - Restricted Mode", func(t *testing.T) {
|
|
cfg := &ActionsConfig{
|
|
TokenPermissionMode: ActionsTokenPermissionModeRestricted,
|
|
}
|
|
defaultPerms := cfg.GetDefaultTokenPermissions()
|
|
perms := cfg.ClampPermissions(defaultPerms)
|
|
assert.Equal(t, perm.AccessModeRead, perms.UnitAccessModes[unit.TypeCode])
|
|
assert.Equal(t, perm.AccessModeNone, perms.UnitAccessModes[unit.TypeIssues])
|
|
assert.Equal(t, perm.AccessModeRead, perms.UnitAccessModes[unit.TypePackages])
|
|
})
|
|
|
|
t.Run("Clamp Permissions", func(t *testing.T) {
|
|
cfg := &ActionsConfig{
|
|
MaxTokenPermissions: &ActionsTokenPermissions{
|
|
UnitAccessModes: map[unit.Type]perm.AccessMode{
|
|
unit.TypeCode: perm.AccessModeRead,
|
|
unit.TypeIssues: perm.AccessModeWrite,
|
|
unit.TypePullRequests: perm.AccessModeRead,
|
|
unit.TypePackages: perm.AccessModeRead,
|
|
unit.TypeActions: perm.AccessModeNone,
|
|
unit.TypeWiki: perm.AccessModeWrite,
|
|
},
|
|
},
|
|
}
|
|
input := ActionsTokenPermissions{
|
|
UnitAccessModes: map[unit.Type]perm.AccessMode{
|
|
unit.TypeCode: perm.AccessModeWrite, // Should be clamped to Read
|
|
unit.TypeIssues: perm.AccessModeWrite, // Should stay Write
|
|
unit.TypePullRequests: perm.AccessModeWrite, // Should be clamped to Read
|
|
unit.TypePackages: perm.AccessModeWrite, // Should be clamped to Read
|
|
unit.TypeActions: perm.AccessModeRead, // Should be clamped to None
|
|
unit.TypeWiki: perm.AccessModeRead, // Should stay Read
|
|
},
|
|
IDToken: true,
|
|
}
|
|
clamped := cfg.ClampPermissions(input)
|
|
assert.Equal(t, perm.AccessModeRead, clamped.UnitAccessModes[unit.TypeCode])
|
|
assert.Equal(t, perm.AccessModeWrite, clamped.UnitAccessModes[unit.TypeIssues])
|
|
assert.Equal(t, perm.AccessModeRead, clamped.UnitAccessModes[unit.TypePullRequests])
|
|
assert.Equal(t, perm.AccessModeRead, clamped.UnitAccessModes[unit.TypePackages])
|
|
assert.Equal(t, perm.AccessModeNone, clamped.UnitAccessModes[unit.TypeActions])
|
|
assert.Equal(t, perm.AccessModeRead, clamped.UnitAccessModes[unit.TypeWiki])
|
|
assert.False(t, clamped.IDToken)
|
|
})
|
|
}
|