refactor: remove UnmarshalHandleDoubleEncode patch and fix repo unit TODO (#39998)

Remove the `UnmarshalHandleDoubleEncode` runtime workaround for an old
xorm bug, and normalize legacy double-encoded JSON in `repo_unit.config`
and `login_source.cfg` with a migration instead.

- Config `FromDB` no longer fails on corrupted values. A broken auth
source is deactivated, so admins can still fix it in the admin panel
- Remove `ProjectsConfig.GetProjectsMode`, `FromDB` already defaults
`ProjectsMode`

Actually it doesn't need so many changes, just because AI thinks it
should handle "corrupted database value" for auth sources.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
wxiaoguangandsilverwind authored and GitHub committed 2026-11-04 05:28:49 +00:00
1 parent 666e6e2d17
commit d919301d36
17 files changed
+318 -145

No files matched your search

+1
View File
@@ -441,6 +441,7 @@ func prepareMigrationTasks() []*migration {
newMigration(362, "Add reusable_workflow_ref column to action_run_job", v29.AddReusableWorkflowRefToActionRunJob),
newMigration(363, "Add release_id to reaction table", v29.AddReleaseIDToReaction),
newMigration(364, "Add new table project_workflow", v29.AddProjectWorkflow),
newMigration(365, "Normalize legacy repository unit and login source JSON", v29.NormalizeLegacyConfigJSON),
}
return preparedMigrations
}
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v29
import (
"context"
"encoding/binary"
"gitea.dev/modelmigration/base"
"gitea.dev/modules/json"
"gitea.dev/modules/util"
)
// unmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
func unmarshalHandleDoubleEncode(bs []byte, v any) error {
if len(bs) == 0 {
// json.Unmarshal will report errors if input is empty (nil or zero-length)
// It seems that XORM ignores the nil but still passes zero-length string into this function
// To be consistent, we should treat all empty inputs as success
return nil
}
err := json.Unmarshal(bs, v)
if err != nil {
ok := true
rs := []byte{}
temp := make([]byte, 2)
for _, rn := range string(bs) {
if rn > 0xffff {
ok = false
break
}
binary.LittleEndian.PutUint16(temp, uint16(rn))
rs = append(rs, temp...)
}
if ok {
if len(rs) > 1 && rs[0] == 0xff && rs[1] == 0xfe {
rs = rs[2:]
}
err = json.Unmarshal(rs, v)
}
}
if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
err = json.Unmarshal(bs[2:], v)
}
return err
}
func normalizeLegacyConfigJSON(config string) string {
if config == "" || json.Valid(util.UnsafeStringToBytes(config)) {
return config
}
m := map[any]any{}
err := unmarshalHandleDoubleEncode(util.UnsafeStringToBytes(config), &m)
if err != nil {
return config
}
if bs, err := json.Marshal(m); err == nil {
return string(bs)
}
return config
}
func NormalizeLegacyConfigJSON(ctx context.Context, x base.EngineMigration) error {
type RepoUnit struct {
ID int64
Config string
}
type LoginSource struct {
ID int64
Cfg string
}
return base.EngineTransaction(x, func(sess base.Session) error {
if err := base.IterateTableByColumn(ctx, x, sess, "id", nil, func(_ context.Context, unit *RepoUnit) error {
config := normalizeLegacyConfigJSON(unit.Config)
if config == unit.Config {
return nil
}
_, err := sess.ID(unit.ID).Cols("config").Update(&RepoUnit{Config: config})
return err
}); err != nil {
return err
}
return base.IterateTableByColumn(ctx, x, sess, "id", nil, func(_ context.Context, source *LoginSource) error {
config := normalizeLegacyConfigJSON(source.Cfg)
if config == source.Cfg {
return nil
}
_, err := sess.ID(source.ID).Cols("cfg").Update(&LoginSource{Cfg: config})
return err
})
})
}
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v29
import (
"testing"
"gitea.dev/modelmigration/migrationtest"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNormalizeLegacyConfigJSONValue(t *testing.T) {
const (
config = `{"Host":"smtp.example.com"}`
packed = "≻潈瑳㨢猢瑭⹰硥浡汰⹥潣≭⁽"
)
for _, tc := range []struct {
name, input, want string
}{
{"empty", "", ""},
{"valid", config + " ", config + " "},
{"unicode and large number", ` {"name":"例😀","id":9007199254740993} `, ` {"name":"例😀","id":9007199254740993} `},
{"double encoded", packed, config},
{"double encoded with BOM", "\ufeff" + packed, config},
{"BOM", "\xff\xfe" + config, config},
{"BOM with non-BMP rune", "\xff\xfe" + `{"name":"😀"}`, `{"name":"😀"}`},
{"corrupt", "invalid", "invalid"},
{"corrupt non-BMP rune", "invalid😀", "invalid😀"},
} {
t.Run(tc.name, func(t *testing.T) {
got := normalizeLegacyConfigJSON(tc.input)
assert.Equal(t, tc.want, got)
assert.Equal(t, got, normalizeLegacyConfigJSON(got))
})
}
}
func TestNormalizeLegacyConfigJSON(t *testing.T) {
type RepoUnit struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64
Config string `xorm:"TEXT"`
}
type LoginSource struct {
ID int64 `xorm:"pk autoincr"`
Name string
Cfg string `xorm:"TEXT"`
}
x, cleanup := migrationtest.PrepareTestEnv(t, 0, new(RepoUnit), new(LoginSource))
defer cleanup()
if x == nil || t.Failed() {
return
}
defer test.MockVariableValue(&setting.Database.IterateBufferSize, 2)()
inputs := []string{
` {"id":9007199254740993,"name":"例😀"} `,
"≻潈瑳㨢猢瑭⹰硥浡汰⹥潣≭⁽",
"\ufeff≻潈瑳㨢猢瑭⹰硥浡汰⹥潣≭⁽",
"",
"invalid",
}
want := []string{inputs[0], `{"Host":"smtp.example.com"}`, `{"Host":"smtp.example.com"}`, "", "invalid", ""}
for _, input := range inputs {
_, err := x.Insert(&RepoUnit{RepoID: 42, Config: input})
require.NoError(t, err)
_, err = x.Insert(&LoginSource{Name: "test", Cfg: input})
require.NoError(t, err)
}
_, err := x.Exec("INSERT INTO `repo_unit` (`repo_id`, `config`) VALUES (?, NULL)", 42)
require.NoError(t, err)
_, err = x.Exec("INSERT INTO `login_source` (`name`, `cfg`) VALUES (?, NULL)", "test")
require.NoError(t, err)
for range 2 {
require.NoError(t, NormalizeLegacyConfigJSON(t.Context(), x))
var units []RepoUnit
require.NoError(t, x.Asc("id").Find(&units))
require.Len(t, units, len(want))
var sources []LoginSource
require.NoError(t, x.Asc("id").Find(&sources))
require.Len(t, sources, len(want))
for i, config := range want {
assert.Equal(t, config, units[i].Config)
assert.EqualValues(t, 42, units[i].RepoID)
assert.Equal(t, config, sources[i].Cfg)
assert.Equal(t, "test", sources[i].Name)
}
}
}
+2 -2
View File
@@ -6,10 +6,10 @@ package actions
import (
"context"
"gitea.dev/models/db"
"gitea.dev/models/perm"
repo_model "gitea.dev/models/repo"
user_model "gitea.dev/models/user"
"gitea.dev/modules/json"
"gitea.dev/modules/util"
"xorm.io/xorm/convert"
@@ -30,7 +30,7 @@ type OwnerActionsConfig struct {
var _ convert.ConversionFrom = (*OwnerActionsConfig)(nil)
func (cfg *OwnerActionsConfig) FromDB(bytes []byte) error {
_ = json.Unmarshal(bytes, cfg)
db.UnmarshalFromDB(bytes, cfg)
cfg.TokenPermissionMode, _ = util.EnumValue(cfg.TokenPermissionMode)
return nil
}
+30 -3
View File
@@ -10,6 +10,7 @@ import (
"reflect"
"gitea.dev/models/db"
"gitea.dev/modules/graceful"
"gitea.dev/modules/log"
"gitea.dev/modules/optional"
"gitea.dev/modules/setting"
@@ -60,14 +61,40 @@ var Names = map[Type]string{
type Config interface {
convert.Conversion
SetAuthSource(*Source)
GetAuthSource() *Source
}
type ConfigBase struct {
AuthSource *Source
AuthSource *Source `json:"-"`
}
func (p *ConfigBase) SetAuthSource(s *Source) {
p.AuthSource = s
func (c *ConfigBase) SetAuthSource(source *Source) {
c.AuthSource = source
}
func (c *ConfigBase) GetAuthSource() *Source {
return c.AuthSource
}
func UnmarshalAuthSourceConfig(bs []byte, v Config) {
if db.UnmarshalFromDB(bs, &v) {
return
}
// This is only a workaround because we don't know whether this unmarshal happens on sign-in page or admin panel.
// * Admin should have the chance to edit the auth source in admin panel, so FromDB should not return an error
// * The sign-in page should not use the auth source in the next login attempt, but we don't have a clear way to disable it immediately (acceptable)
// Actually, corrupted database value is a really rare case, just because AI thinks it should be handled.
authSourceID := v.GetAuthSource().ID
log.Error("Unable to unmarshal login source id=%d, will mark it as inactive to avoid unexpected sign-in issues", authSourceID)
go graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
// A small time window to avoid race condition with admin's editing because there is no other way.
// The UpdateUnix field is not filled when this function is called.
_, err := db.GetEngine(ctx).ID(authSourceID).And("is_active = ? AND updated_unix < ?", true, timeutil.TimeStampNow()-5).
Cols("is_active").Update(&Source{IsActive: false})
if err != nil {
log.Error("Unable to set login source id=%d to inactive: %v", authSourceID, err)
}
})
}
// SkipVerifiable configurations provide a IsSkipVerify to check if SkipVerify is set
+41 -1
View File
@@ -6,10 +6,15 @@ package auth_test
import (
"strings"
"testing"
"time"
auth_model "gitea.dev/models/auth"
"gitea.dev/models/db"
"gitea.dev/models/unittest"
"gitea.dev/modules/json"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"gitea.dev/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -24,7 +29,8 @@ type TestSource struct {
// FromDB fills up a LDAPConfig from serialized format.
func (source *TestSource) FromDB(bs []byte) error {
return json.Unmarshal(bs, &source)
auth_model.UnmarshalAuthSourceConfig(bs, source)
return nil
}
// ToDB exports a LDAPConfig to a serialized format.
@@ -53,3 +59,37 @@ func TestDumpAuthSource(t *testing.T) {
// INSERT INTO `login_source` (`id`, `type`, `name`, `is_active`, `is_sync_enabled`, `two_factor_policy`, `cfg`, `created_unix`, `updated_unix`) VALUES (1,6,'TestSource',0,0,'','{"TestField":"TestValue"}',1774179784,1774179784);
assert.Contains(t, sb.String(), `'{"TestField":"TestValue"}'`)
}
func TestUnmarshalAuthSourceConfig(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.IsProd, true)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
cfg := &TestSource{}
source := &auth_model.Source{Type: auth_model.OAuth2, Name: t.Name(), IsActive: true, Cfg: cfg}
err := db.Insert(t.Context(), source)
require.NoError(t, err)
cfg.SetAuthSource(source)
t.Run("ValidConfig", func(t *testing.T) {
for _, data := range []string{`{"TestField":"TestValue"}`, "", "null"} {
auth_model.UnmarshalAuthSourceConfig([]byte(data), cfg)
assert.Equal(t, "TestValue", cfg.TestField)
assert.Same(t, source, cfg.GetAuthSource())
}
})
t.Run("InvalidConfigDeactivatesSource", func(t *testing.T) {
_, err = db.GetEngine(t.Context()).ID(source.ID).NoAutoTime().Cols("updated_unix").Update(&auth_model.Source{UpdatedUnix: timeutil.TimeStampNow() - 60})
require.NoError(t, err)
has, _ := db.GetEngine(t.Context()).ID(source.ID).And("is_active=?", true).Get(&auth_model.Source{})
assert.True(t, has)
auth_model.UnmarshalAuthSourceConfig([]byte(`{"TestField":false}`), cfg)
assert.Same(t, source, cfg.GetAuthSource())
require.Eventually(t, func() bool {
has, _ := db.GetEngine(t.Context()).ID(source.ID).And("is_active=?", false).Get(&auth_model.Source{})
return has
}, time.Second, time.Millisecond)
})
}
+19
View File
@@ -6,6 +6,7 @@ package db
import (
"fmt"
"gitea.dev/modules/json"
"gitea.dev/modules/setting"
"xorm.io/xorm"
@@ -84,3 +85,21 @@ func CellToInt[T ~int | int64](cell xorm.Cell, def T) (ret T, has bool, err erro
}
return T(val), true, err
}
func UnmarshalFromDB(data []byte, v any) bool {
if len(data) == 0 {
return true
}
err := json.Unmarshal(data, v)
if err != nil {
// Even if database value is corrupted, we should not let it cause unnecessary 500 error or block user's operation
// Corrupted database value is a really rare case, it should never happen unless site admin manually modified the database.
// Although some database values will be printed into logs, it isn't a real problem
// because such case should never really happen. If it happens, logging the value
// is the only way to tell site admin which record is wrong since there is no other context information,
// and then they need to re-create the config (with new secrets)
setting.PanicInDevOrTesting("UnmarshalFromDB %q failed: %v", string(data), err)
return false
}
return true
}
+13 -35
View File
@@ -56,12 +56,11 @@ func init() {
// UnitConfig describes common unit config
type UnitConfig struct{}
// FromDB fills up a UnitConfig from serialized format.
func (cfg *UnitConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
return nil
}
// ToDB exports a UnitConfig to a serialized format.
func (cfg *UnitConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
@@ -71,12 +70,11 @@ type ExternalWikiConfig struct {
ExternalWikiURL string
}
// FromDB fills up a ExternalWikiConfig from serialized format.
func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
return nil
}
// ToDB exports a ExternalWikiConfig to a serialized format.
func (cfg *ExternalWikiConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
@@ -89,12 +87,11 @@ type ExternalTrackerConfig struct {
ExternalTrackerRegexpPattern string
}
// FromDB fills up a ExternalTrackerConfig from serialized format.
func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
return nil
}
// ToDB exports a ExternalTrackerConfig to a serialized format.
func (cfg *ExternalTrackerConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
@@ -106,12 +103,11 @@ type IssuesConfig struct {
EnableDependencies bool
}
// FromDB fills up a IssuesConfig from serialized format.
func (cfg *IssuesConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
return nil
}
// ToDB exports a IssuesConfig to a serialized format.
func (cfg *IssuesConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
@@ -155,16 +151,14 @@ func DefaultPullRequestsConfig() *PullRequestsConfig {
return cfg
}
// FromDB fills up a PullRequestsConfig from serialized format.
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
// set default values for existing PullRequestConfig in DB
*cfg = *DefaultPullRequestsConfig()
_ = json.UnmarshalHandleDoubleEncode(bs, &cfg) // don't let corrupted database value cause unnecessary 500 error
db.UnmarshalFromDB(bs, cfg)
cfg.DefaultUpdateStyle = util.IfZero(cfg.DefaultUpdateStyle, UpdateStyleMerge)
return nil
}
// ToDB exports a PullRequestsConfig to a serialized format.
func (cfg *PullRequestsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
@@ -228,34 +222,18 @@ type ProjectsConfig struct {
ProjectsMode ProjectsMode
}
// FromDB fills up a ProjectsConfig from serialized format.
func (cfg *ProjectsConfig) FromDB(bs []byte) error {
// TODO: remove GetProjectsMode, only use ProjectsMode
cfg.ProjectsMode = ProjectsModeAll
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
cfg.ProjectsMode = util.IfZero(cfg.ProjectsMode, ProjectsModeAll)
return nil
}
// ToDB exports a ProjectsConfig to a serialized format.
func (cfg *ProjectsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
func (cfg *ProjectsConfig) GetProjectsMode() ProjectsMode {
if cfg.ProjectsMode != "" {
return cfg.ProjectsMode
}
return ProjectsModeAll
}
func (cfg *ProjectsConfig) IsProjectsAllowed(m ProjectsMode) bool {
projectsMode := cfg.GetProjectsMode()
if m == ProjectsModeNone {
return true
}
return projectsMode == m || projectsMode == ProjectsModeAll
return m == ProjectsModeNone || cfg.ProjectsMode == m || cfg.ProjectsMode == ProjectsModeAll
}
// BeforeSet is invoked from XORM before setting the value of a field of this object.
+2 -3
View File
@@ -6,6 +6,7 @@ package repo
import (
"slices"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
"gitea.dev/modules/json"
@@ -173,14 +174,12 @@ func (cfg *ActionsConfig) ClampPermissions(perms ActionsTokenPermissions) Action
return ClampActionsTokenPermissions(perms, maxPerms)
}
// FromDB fills up a ActionsConfig from serialized format.
func (cfg *ActionsConfig) FromDB(bs []byte) error {
_ = json.UnmarshalHandleDoubleEncode(bs, &cfg)
db.UnmarshalFromDB(bs, cfg)
cfg.TokenPermissionMode, _ = util.EnumValue(cfg.TokenPermissionMode)
return nil
}
// ToDB exports a ActionsConfig to a serialized format.
func (cfg *ActionsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
-36
View File
@@ -5,7 +5,6 @@ package json
import (
"bytes"
"encoding/binary"
"encoding/json" //nolint:depguard // this package wraps it
"io"
)
@@ -81,38 +80,3 @@ func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
func Valid(data []byte) bool {
return json.Valid(data)
}
// UnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
func UnmarshalHandleDoubleEncode(bs []byte, v any) error {
if len(bs) == 0 {
// json.Unmarshal will report errors if input is empty (nil or zero-length)
// It seems that XORM ignores the nil but still passes zero-length string into this function
// To be consistent, we should treat all empty inputs as success
return nil
}
err := json.Unmarshal(bs, v)
if err != nil {
ok := true
rs := []byte{}
temp := make([]byte, 2)
for _, rn := range string(bs) {
if rn > 0xffff {
ok = false
break
}
binary.LittleEndian.PutUint16(temp, uint16(rn))
rs = append(rs, temp...)
}
if ok {
if len(rs) > 1 && rs[0] == 0xff && rs[1] == 0xfe {
rs = rs[2:]
}
err = json.Unmarshal(rs, v)
}
}
if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
err = json.Unmarshal(bs[2:], v)
}
return err
}
-8
View File
@@ -10,14 +10,6 @@ import (
"github.com/stretchr/testify/assert"
)
func TestGiteaDBJSONUnmarshal(t *testing.T) {
var m map[any]any
err := UnmarshalHandleDoubleEncode(nil, &m)
assert.NoError(t, err)
err = UnmarshalHandleDoubleEncode([]byte(""), &m)
assert.NoError(t, err)
}
func TestIndent(t *testing.T) {
buf := &bytes.Buffer{}
err := Indent(buf, []byte(`{"a":1}`), ">", " ")
+3 -15
View File
@@ -13,14 +13,7 @@ import (
"gitea.dev/modules/setting"
)
// .____ ________ _____ __________
// | | \______ \ / _ \\______ \
// | | | | \ / /_\ \| ___/
// | |___ | ` \/ | \ |
// |_______ \/_______ /\____|__ /____|
// \/ \/ \/
// Package ldap provide functions & structure to query a LDAP ldap directory
// Package ldap provide functions & structure to query an LDAP directory
// For now, it's mainly tested again an MS Active Directory service, see README.md for more information
// Source Basic LDAP authentication service
@@ -60,12 +53,8 @@ type Source struct {
UserUID string // User Attribute listed in Group
}
// FromDB fills up a LDAPConfig from serialized format.
func (source *Source) FromDB(bs []byte) error {
err := json.UnmarshalHandleDoubleEncode(bs, &source)
if err != nil {
return err
}
func (source *Source) FromDB(bs []byte) (err error) {
auth.UnmarshalAuthSourceConfig(bs, source)
if source.BindPasswordEncrypt != "" {
source.BindPassword, err = secret.DecryptSecret(setting.SecretKey, source.BindPasswordEncrypt)
if err != nil {
@@ -76,7 +65,6 @@ func (source *Source) FromDB(bs []byte) error {
return nil
}
// ToDB exports a LDAPConfig to a serialized format.
func (source *Source) ToDB() ([]byte, error) {
var err error
source.BindPasswordEncrypt, err = secret.EncryptSecret(setting.SecretKey, source.BindPassword)
+2 -3
View File
@@ -33,12 +33,11 @@ type Source struct {
ExternalIDClaim string
}
// FromDB fills up an OAuth2Config from serialized format.
func (source *Source) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &source)
auth.UnmarshalAuthSourceConfig(bs, source)
return nil
}
// ToDB exports an OAuth2Config to a serialized format.
func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
+2 -10
View File
@@ -8,13 +8,6 @@ import (
"gitea.dev/modules/json"
)
// __________ _____ _____
// \______ \/ _ \ / \
// | ___/ /_\ \ / \ / \
// | | / | \/ Y \
// |____| \____|__ /\____|__ /
// \/ \/
// Source holds configuration for the PAM login source.
type Source struct {
auth.ConfigBase `json:"-"`
@@ -23,12 +16,11 @@ type Source struct {
EmailDomain string
}
// FromDB fills up a PAMConfig from serialized format.
func (source *Source) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &source)
auth.UnmarshalAuthSourceConfig(bs, source)
return nil
}
// ToDB exports a PAMConfig to a serialized format.
func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
+2 -10
View File
@@ -8,13 +8,6 @@ import (
"gitea.dev/modules/json"
)
// _________ __________________________
// / _____/ / \__ ___/\______ \
// \_____ \ / \ / \| | | ___/
// / \/ Y \ | | |
// /_______ /\____|__ /____| |____|
// \/ \/
// Source holds configuration for the SMTP login source.
type Source struct {
auth.ConfigBase `json:"-"`
@@ -29,12 +22,11 @@ type Source struct {
DisableHelo bool
}
// FromDB fills up an SMTPConfig from serialized format.
func (source *Source) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &source)
auth.UnmarshalAuthSourceConfig(bs, source)
return nil
}
// ToDB exports an SMTPConfig to a serialized format.
func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
+5 -13
View File
@@ -8,13 +8,6 @@ import (
"gitea.dev/modules/json"
)
// _________ ___________________.___
// / _____// _____/\______ \ |
// \_____ \ \_____ \ | ___/ |
// / \/ \ | | | |
// /_______ /_______ / |____| |___|
// \/ \/
// Source holds configuration for SSPI single sign-on.
type Source struct {
auth.ConfigBase `json:"-"`
@@ -26,14 +19,13 @@ type Source struct {
DefaultLanguage string
}
// FromDB fills up an SSPIConfig from serialized format.
func (cfg *Source) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
func (source *Source) FromDB(bs []byte) error {
auth.UnmarshalAuthSourceConfig(bs, source)
return nil
}
// ToDB exports an SSPIConfig to a serialized format.
func (cfg *Source) ToDB() ([]byte, error) {
return json.Marshal(cfg)
func (source *Source) ToDB() ([]byte, error) {
return json.Marshal(source)
}
func init() {
+6 -6
View File
@@ -466,19 +466,19 @@
</p>
<div class="ui dropdown selection">
<select name="projects_mode">
<option value="repo" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.GetProjectsMode "repo")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}</option>
<option value="owner" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.GetProjectsMode "owner")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}</option>
<option value="all" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.GetProjectsMode "all")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}</option>
<option value="repo" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "repo")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}</option>
<option value="owner" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}</option>
<option value="all" {{if or (not $isProjectsEnabled) (eq $projectsUnit.ProjectsConfig.ProjectsMode "all")}}selected{{end}}>{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}</option>
</select>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="default text">
{{if (eq $projectsUnit.ProjectsConfig.GetProjectsMode "repo")}}
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "repo")}}
{{ctx.Locale.Tr "repo.settings.projects_mode_repo"}}
{{end}}
{{if (eq $projectsUnit.ProjectsConfig.GetProjectsMode "owner")}}
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "owner")}}
{{ctx.Locale.Tr "repo.settings.projects_mode_owner"}}
{{end}}
{{if (eq $projectsUnit.ProjectsConfig.GetProjectsMode "all")}}
{{if (eq $projectsUnit.ProjectsConfig.ProjectsMode "all")}}
{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}
{{end}}
</div>