Merge pull request '[gitea] week 2024-21-v7.0 cherry pick (release/v1.22 -> v7.0/forgejo)' (#3859) from earl-warren/wcp/2024-21-v7.0 into v7.0/forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3859 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Gergely Nagy <algernon@noreply.codeberg.org>
This commit is contained in:
commit
6bb1b001f9
8 changed files with 54 additions and 28 deletions
|
@ -124,16 +124,15 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||||
OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
|
OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !OAuth2.Enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
|
|
||||||
|
|
||||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
||||||
|
// Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems).
|
||||||
|
// Including: CSRF token, account validation token, etc ...
|
||||||
|
// In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
||||||
|
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
|
||||||
if InstallLock {
|
if InstallLock {
|
||||||
jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64)
|
jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -155,8 +154,6 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generalSigningSecret is used as container for a []byte value
|
|
||||||
// instead of an additional mutex, we use CompareAndSwap func to change the value thread save
|
|
||||||
var generalSigningSecret atomic.Pointer[[]byte]
|
var generalSigningSecret atomic.Pointer[[]byte]
|
||||||
|
|
||||||
func GetGeneralTokenSigningSecret() []byte {
|
func GetGeneralTokenSigningSecret() []byte {
|
||||||
|
@ -164,11 +161,9 @@ func GetGeneralTokenSigningSecret() []byte {
|
||||||
if old == nil || len(*old) == 0 {
|
if old == nil || len(*old) == 0 {
|
||||||
jwtSecret, _, err := generate.NewJwtSecret()
|
jwtSecret, _, err := generate.NewJwtSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to generate general JWT secret: %s", err.Error())
|
log.Fatal("Unable to generate general JWT secret: %v", err)
|
||||||
}
|
}
|
||||||
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
||||||
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
|
|
||||||
log.Warn("OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
|
|
||||||
return jwtSecret
|
return jwtSecret
|
||||||
}
|
}
|
||||||
return *generalSigningSecret.Load()
|
return *generalSigningSecret.Load()
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package setting
|
package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
|
@ -14,7 +15,7 @@ import (
|
||||||
|
|
||||||
func TestGetGeneralSigningSecret(t *testing.T) {
|
func TestGetGeneralSigningSecret(t *testing.T) {
|
||||||
// when there is no general signing secret, it should be generated, and keep the same value
|
// when there is no general signing secret, it should be generated, and keep the same value
|
||||||
assert.Nil(t, generalSigningSecret.Load())
|
generalSigningSecret.Store(nil)
|
||||||
s1 := GetGeneralTokenSigningSecret()
|
s1 := GetGeneralTokenSigningSecret()
|
||||||
assert.NotNil(t, s1)
|
assert.NotNil(t, s1)
|
||||||
s2 := GetGeneralTokenSigningSecret()
|
s2 := GetGeneralTokenSigningSecret()
|
||||||
|
@ -32,3 +33,28 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
assert.Len(t, actual, 32)
|
assert.Len(t, actual, 32)
|
||||||
assert.EqualValues(t, expected, actual)
|
assert.EqualValues(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetGeneralSigningSecretSave(t *testing.T) {
|
||||||
|
defer test.MockVariableValue(&InstallLock, true)()
|
||||||
|
|
||||||
|
old := GetGeneralTokenSigningSecret()
|
||||||
|
assert.Len(t, old, 32)
|
||||||
|
|
||||||
|
tmpFile := t.TempDir() + "/app.ini"
|
||||||
|
_ = os.WriteFile(tmpFile, nil, 0o644)
|
||||||
|
cfg, _ := NewConfigProviderFromFile(tmpFile)
|
||||||
|
loadOAuth2From(cfg)
|
||||||
|
generated := GetGeneralTokenSigningSecret()
|
||||||
|
assert.Len(t, generated, 32)
|
||||||
|
assert.NotEqual(t, old, generated)
|
||||||
|
|
||||||
|
generalSigningSecret.Store(nil)
|
||||||
|
cfg, _ = NewConfigProviderFromFile(tmpFile)
|
||||||
|
loadOAuth2From(cfg)
|
||||||
|
again := GetGeneralTokenSigningSecret()
|
||||||
|
assert.Equal(t, generated, again)
|
||||||
|
|
||||||
|
iniContent, err := os.ReadFile(tmpFile)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, string(iniContent), "JWT_SECRET = ")
|
||||||
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ type EditRepoOption struct {
|
||||||
Archived *bool `json:"archived,omitempty"`
|
Archived *bool `json:"archived,omitempty"`
|
||||||
// set to a string like `8h30m0s` to set the mirror interval time
|
// set to a string like `8h30m0s` to set the mirror interval time
|
||||||
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
||||||
// enable prune - remove obsolete remote-tracking references
|
// enable prune - remove obsolete remote-tracking references when mirroring
|
||||||
EnablePrune *bool `json:"enable_prune,omitempty"`
|
EnablePrune *bool `json:"enable_prune,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3413,7 +3413,7 @@ mirror_sync_create = synced new reference <a href="%[2]s">%[3]s</a> to <a href="
|
||||||
mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
|
mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
|
||||||
approve_pull_request = `approved <a href="%[1]s">%[3]s#%[2]s</a>`
|
approve_pull_request = `approved <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||||
reject_pull_request = `suggested changes for <a href="%[1]s">%[3]s#%[2]s</a>`
|
reject_pull_request = `suggested changes for <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||||
publish_release = `released <a href="%[2]s"> "%[4]s" </a> at <a href="%[1]s">%[3]s</a>`
|
publish_release = `released <a href="%[2]s">%[4]s</a> at <a href="%[1]s">%[3]s</a>`
|
||||||
review_dismissed = `dismissed review from <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
|
review_dismissed = `dismissed review from <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||||
review_dismissed_reason = Reason:
|
review_dismissed_reason = Reason:
|
||||||
create_branch = created branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
|
create_branch = created branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
|
||||||
|
|
8
poetry.lock
generated
8
poetry.lock
generated
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
|
@ -318,13 +318,13 @@ files = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tqdm"
|
name = "tqdm"
|
||||||
version = "4.66.2"
|
version = "4.66.4"
|
||||||
description = "Fast, Extensible Progress Meter"
|
description = "Fast, Extensible Progress Meter"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
|
{file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"},
|
||||||
{file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
|
{file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
|
@ -1054,16 +1054,10 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
|
||||||
func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
|
func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
// only update mirror if interval or enable prune are provided
|
// Skip this update if the repo is not a mirror, do not return error.
|
||||||
if opts.MirrorInterval == nil && opts.EnablePrune == nil {
|
// Because reporting errors only makes the logic more complex&fragile, it doesn't really help end users.
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// these values only make sense if the repo is a mirror
|
|
||||||
if !repo.IsMirror {
|
if !repo.IsMirror {
|
||||||
err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
|
return nil
|
||||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the mirror from the repo
|
// get the mirror from the repo
|
||||||
|
|
|
@ -486,6 +486,17 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
|
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
||||||
|
// see the "loadOAuth2From" in "setting/oauth2.go"
|
||||||
|
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
|
||||||
|
_, jwtSecretBase64, err := generate.NewJwtSecret()
|
||||||
|
if err != nil {
|
||||||
|
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
|
||||||
|
}
|
||||||
|
|
||||||
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
|
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
|
||||||
if setting.SecretKey == "" {
|
if setting.SecretKey == "" {
|
||||||
var secretKey string
|
var secretKey string
|
||||||
|
|
2
templates/swagger/v1_json.tmpl
generated
2
templates/swagger/v1_json.tmpl
generated
|
@ -20115,7 +20115,7 @@
|
||||||
"x-go-name": "Description"
|
"x-go-name": "Description"
|
||||||
},
|
},
|
||||||
"enable_prune": {
|
"enable_prune": {
|
||||||
"description": "enable prune - remove obsolete remote-tracking references",
|
"description": "enable prune - remove obsolete remote-tracking references when mirroring",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"x-go-name": "EnablePrune"
|
"x-go-name": "EnablePrune"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue