mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-14 00:01:44 -04:00
Compare commits
No commits in common. "2b3f12f6fd12afebb3b8397dc612621df6c730e2" and "d5e417a33d04d7a2d16317495d7aad45ca0868ed" have entirely different histories.
2b3f12f6fd
...
d5e417a33d
138
.drone.yml
138
.drone.yml
@ -985,10 +985,7 @@ depends_on:
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
include:
|
||||
- "refs/tags/**"
|
||||
exclude:
|
||||
- "refs/tags/**-rc*"
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
@ -1036,68 +1033,6 @@ steps:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
---
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-linux-amd64-release-candidate-version
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
depends_on:
|
||||
- testing-amd64
|
||||
- testing-arm64
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- "refs/tags/**-rc*"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
tags: ${DRONE_TAG##v}-linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
- name: publish-rootless
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
tags: ${DRONE_TAG##v}-linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
@ -1274,10 +1209,7 @@ depends_on:
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
include:
|
||||
- "refs/tags/**"
|
||||
exclude:
|
||||
- "refs/tags/**-rc*"
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
@ -1326,68 +1258,6 @@ steps:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-linux-arm64-release-candidate-version
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
depends_on:
|
||||
- testing-amd64
|
||||
- testing-arm64
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- "refs/tags/**-rc*"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
tags: ${DRONE_TAG##v}-linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
- name: publish-rootless
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
tags: ${DRONE_TAG##v}-linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
@ -1557,9 +1427,7 @@ trigger:
|
||||
|
||||
depends_on:
|
||||
- docker-linux-amd64-release-version
|
||||
- docker-linux-amd64-release-candidate-version
|
||||
- docker-linux-arm64-release-version
|
||||
- docker-linux-arm64-release-candidate-version
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
@ -1641,8 +1509,6 @@ depends_on:
|
||||
- docker-linux-arm64-release
|
||||
- docker-linux-amd64-release-version
|
||||
- docker-linux-arm64-release-version
|
||||
- docker-linux-amd64-release-candidate-version
|
||||
- docker-linux-arm64-release-candidate-version
|
||||
- docker-linux-amd64-release-branch
|
||||
- docker-linux-arm64-release-branch
|
||||
- docker-manifest
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
pwd "code.gitea.io/gitea/modules/auth/password"
|
||||
pwd "code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
pwd "code.gitea.io/gitea/modules/auth/password"
|
||||
pwd "code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -57,10 +57,9 @@ func confirm() (bool, error) {
|
||||
}
|
||||
|
||||
func initDB(ctx context.Context) error {
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadDBSetting()
|
||||
setting.InitSQLLog(false)
|
||||
setting.LoadFromExisting()
|
||||
setting.InitDBConfig()
|
||||
setting.NewXORMLogService(false)
|
||||
|
||||
if setting.Database.Type == "" {
|
||||
log.Fatal(`Database settings are missing from the configuration file: %q.
|
||||
|
@ -32,7 +32,7 @@ func runConvert(ctx *cli.Context) error {
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
if !setting.Database.UseMySQL {
|
||||
|
@ -87,16 +87,14 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadDBSetting()
|
||||
setting.LoadFromExisting()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.Log.EnableXORMLog = ctx.Bool("debug")
|
||||
setting.EnableXORMLog = ctx.Bool("debug")
|
||||
setting.Database.LogSQL = ctx.Bool("debug")
|
||||
// FIXME: don't use CfgProvider directly
|
||||
setting.CfgProvider.Section("log").Key("XORM").SetValue(",")
|
||||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
||||
|
||||
setting.InitSQLLog(!ctx.Bool("debug"))
|
||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
|
20
cmd/dump.go
20
cmd/dump.go
@ -181,22 +181,20 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
fileName += "." + outType
|
||||
}
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
|
||||
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||
// FIXME: don't use CfgProvider directly
|
||||
if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil {
|
||||
if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil {
|
||||
fatal("Setting logging mode to console failed: %v", err)
|
||||
}
|
||||
if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil {
|
||||
if _, err := setting.Cfg.Section("log.console").NewKey("STDERR", "true"); err != nil {
|
||||
fatal("Setting console logger to stderr failed: %v", err)
|
||||
}
|
||||
if !setting.InstallLock {
|
||||
log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
|
||||
return fmt.Errorf("gitea is not initialized")
|
||||
}
|
||||
setting.LoadSettings() // cannot access session settings otherwise
|
||||
setting.NewServices() // cannot access session settings otherwise
|
||||
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
@ -324,7 +322,7 @@ func runDump(ctx *cli.Context) error {
|
||||
log.Info("Packing data directory...%s", setting.AppDataPath)
|
||||
|
||||
var excludes []string
|
||||
if setting.SessionConfig.OriginalProvider == "file" {
|
||||
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" {
|
||||
var opts session.Options
|
||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
||||
return err
|
||||
@ -341,7 +339,7 @@ func runDump(ctx *cli.Context) error {
|
||||
excludes = append(excludes, setting.LFS.Path)
|
||||
excludes = append(excludes, setting.Attachment.Path)
|
||||
excludes = append(excludes, setting.Packages.Path)
|
||||
excludes = append(excludes, setting.Log.RootPath)
|
||||
excludes = append(excludes, setting.LogRootPath)
|
||||
excludes = append(excludes, absFileName)
|
||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||
fatal("Failed to include data directory: %v", err)
|
||||
@ -380,12 +378,12 @@ func runDump(ctx *cli.Context) error {
|
||||
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
||||
log.Info("Skip dumping log files")
|
||||
} else {
|
||||
isExist, err := util.IsExist(setting.Log.RootPath)
|
||||
isExist, err := util.IsExist(setting.LogRootPath)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err)
|
||||
log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err)
|
||||
}
|
||||
if isExist {
|
||||
if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil {
|
||||
if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil {
|
||||
fatal("Failed to include log: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
var (
|
||||
|
@ -112,8 +112,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Read configuration file
|
||||
setting.InitProviderAllowEmpty()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadAllowEmpty()
|
||||
|
||||
pats, err := getPatterns(c.Args())
|
||||
if err != nil {
|
||||
|
@ -17,8 +17,7 @@ func runSendMail(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
|
||||
if err := argsSet(c, "title"); err != nil {
|
||||
return err
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -33,7 +33,7 @@ func runMigrate(ctx *cli.Context) error {
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
|
@ -136,7 +136,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
|
@ -54,8 +54,7 @@ func runRestoreRepository(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
var units []string
|
||||
if s := c.String("units"); s != "" {
|
||||
units = strings.Split(s, ",")
|
||||
|
@ -61,8 +61,7 @@ func setup(logPath string, debug bool) {
|
||||
} else {
|
||||
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
|
||||
}
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
if debug {
|
||||
setting.RunMode = "dev"
|
||||
}
|
||||
|
@ -158,8 +158,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
log.Info("Global init")
|
||||
// Perform global initialization
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
||||
|
||||
// We check that AppDataPath exists here (it should have been created during installation)
|
||||
|
@ -49,8 +49,7 @@ func runPR() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAllowEmpty()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadAllowEmpty()
|
||||
|
||||
setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos")
|
||||
if err != nil {
|
||||
@ -83,7 +82,7 @@ func runPR() {
|
||||
setting.Database.Path = ":memory:"
|
||||
setting.Database.Timeout = 500
|
||||
*/
|
||||
dbCfg := setting.CfgProvider.Section("database")
|
||||
dbCfg := setting.Cfg.Section("database")
|
||||
dbCfg.NewKey("DB_TYPE", "sqlite3")
|
||||
dbCfg.NewKey("PATH", ":memory:")
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-rootless
|
||||
{{#if build.tags}}
|
||||
{{#unless contains "-rc" build.tag}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}-rootless
|
||||
{{/each}}
|
||||
- "latest-rootless"
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
|
@ -1,12 +1,10 @@
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}
|
||||
{{#if build.tags}}
|
||||
{{#unless contains "-rc" build.tag }}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
- "latest"
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
|
@ -568,22 +568,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
|
||||
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
|
||||
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
|
||||
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining INTERNAL_TOKEN in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
|
||||
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, pbkdf2_v1, pbkdf2_hi, scrypt, bcrypt\], argon2 and scrypt will spend significant amounts of memory.
|
||||
- Note: The default parameters for `pbkdf2` hashing have changed - the previous settings are available as `pbkdf2_v1` but are not recommended.
|
||||
- The hash functions may be tuned by using `$` after the algorithm:
|
||||
- `argon2$<time>$<memory>$<threads>$<key-length>`
|
||||
- `bcrypt$<cost>`
|
||||
- `pbkdf2$<iterations>$<key-length>`
|
||||
- `scrypt$<n>$<r>$<p>$<key-length>`
|
||||
- The defaults are:
|
||||
- `argon2`: `argon2$2$65536$8$50`
|
||||
- `bcrypt`: `bcrypt$10`
|
||||
- `pbkdf2`: `pbkdf2$50000$50`
|
||||
- `pbkdf2_v1`: `pbkdf2$10000$50`
|
||||
- `pbkdf2_v2`: `pbkdf2$50000$50`
|
||||
- `pbkdf2_hi`: `pbkdf2$320000$50`
|
||||
- `scrypt`: `scrypt$65536$16$2$50`
|
||||
- Adjusting the algorithm parameters using this functionality is done at your own risk.
|
||||
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, scrypt, bcrypt\], argon2 will spend more memory than others.
|
||||
- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
|
||||
- `MIN_PASSWORD_LENGTH`: **6**: Minimum password length for new users.
|
||||
- `PASSWORD_COMPLEXITY`: **off**: Comma separated list of character classes required to pass minimum complexity. If left empty or no valid values are specified, checking is disabled (off):
|
||||
|
@ -1,51 +0,0 @@
|
||||
---
|
||||
date: "2023-02-14T00:00:00+00:00"
|
||||
title: "Guidelines for Refactoring"
|
||||
slug: "guidelines-refactoring"
|
||||
weight: 20
|
||||
toc: false
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "developers"
|
||||
name: "Guidelines for Refactoring"
|
||||
weight: 20
|
||||
identifier: "guidelines-refactoring"
|
||||
---
|
||||
|
||||
# Guidelines for Refactoring
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## Background
|
||||
|
||||
Since the first line of code was written at Feb 12, 2014, Gitea has grown to be a large project.
|
||||
As a result, the codebase has become larger and larger. The larger the codebase is, the more difficult it is to maintain.
|
||||
A lot of outdated mechanisms exist, a lot of frameworks are mixed together, some legacy code might cause bugs and block new features.
|
||||
To make the codebase more maintainable and make Gitea better, developers should keep in mind to use modern mechanisms to refactor the old code.
|
||||
|
||||
This document is a collection of guidelines for refactoring the codebase.
|
||||
|
||||
## Refactoring Suggestions
|
||||
|
||||
* Design more about the future, but not only resolve the current problem.
|
||||
* Reduce ambiguity, reduce conflicts, improve maintainability.
|
||||
* Describe the refactoring, for example:
|
||||
* Why the refactoring is needed.
|
||||
* How the legacy problems would be solved.
|
||||
* What's the Pros/Cons of the refactoring.
|
||||
* Only do necessary changes, keep the old logic as much as possible.
|
||||
* Introduce some intermediate steps to make the refactoring easier to review, a complete refactoring plan could be done in several PRs.
|
||||
* If there is any divergence, the TOC(Technical Oversight Committee) should be involved to help to make decisions.
|
||||
* Add necessary tests to make sure the refactoring is correct.
|
||||
* Non-bug refactoring is preferred to be done at the beginning of a milestone, it would be easier to find problems before the release.
|
||||
|
||||
## Reviewing & Merging Suggestions
|
||||
|
||||
* A refactoring PR shouldn't be kept open for a long time (usually 7 days), it should be reviewed as soon as possible.
|
||||
* A refactoring PR should be merged as soon as possible, it should not be blocked by other PRs.
|
||||
* If there is no objection from TOC, a refactoring PR could be merged after 7 days with one core member's approval (not the author).
|
||||
* Tolerate some dirty/hacky intermediate steps if the final result is good.
|
||||
* Tolerate some regression bugs if the refactoring is necessary, fix bugs as soon as possible.
|
@ -13,7 +13,7 @@ import (
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -25,7 +25,7 @@ func TestIterate(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 83, repoCnt)
|
||||
assert.EqualValues(t, 81, repoCnt)
|
||||
|
||||
err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error {
|
||||
reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID}
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -556,16 +556,3 @@
|
||||
repo_id: 54
|
||||
type: 1
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 82
|
||||
repo_id: 31
|
||||
type: 1
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 83
|
||||
repo_id: 31
|
||||
type: 3
|
||||
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowRebaseMerge\":true,\"AllowSquash\":true}"
|
||||
created_unix: 946684810
|
||||
|
@ -8,8 +8,8 @@
|
||||
email: user1@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user1
|
||||
@ -45,8 +45,8 @@
|
||||
email: user2@example.com
|
||||
keep_email_private: true
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user2
|
||||
@ -82,8 +82,8 @@
|
||||
email: user3@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: onmention
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user3
|
||||
@ -119,8 +119,8 @@
|
||||
email: user4@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: onmention
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user4
|
||||
@ -156,8 +156,8 @@
|
||||
email: user5@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user5
|
||||
@ -193,8 +193,8 @@
|
||||
email: user6@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user6
|
||||
@ -230,8 +230,8 @@
|
||||
email: user7@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: disabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user7
|
||||
@ -267,8 +267,8 @@
|
||||
email: user8@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user8
|
||||
@ -304,8 +304,8 @@
|
||||
email: user9@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: onmention
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user9
|
||||
@ -341,8 +341,8 @@
|
||||
email: user10@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user10
|
||||
@ -378,8 +378,8 @@
|
||||
email: user11@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user11
|
||||
@ -415,8 +415,8 @@
|
||||
email: user12@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user12
|
||||
@ -452,8 +452,8 @@
|
||||
email: user13@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user13
|
||||
@ -489,8 +489,8 @@
|
||||
email: user14@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user14
|
||||
@ -526,8 +526,8 @@
|
||||
email: user15@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user15
|
||||
@ -563,8 +563,8 @@
|
||||
email: user16@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user16
|
||||
@ -600,8 +600,8 @@
|
||||
email: user17@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user17
|
||||
@ -637,8 +637,8 @@
|
||||
email: user18@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user18
|
||||
@ -674,8 +674,8 @@
|
||||
email: user19@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user19
|
||||
@ -711,8 +711,8 @@
|
||||
email: user20@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user20
|
||||
@ -748,8 +748,8 @@
|
||||
email: user21@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user21
|
||||
@ -785,8 +785,8 @@
|
||||
email: limited_org@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: limited_org
|
||||
@ -822,8 +822,8 @@
|
||||
email: privated_org@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: privated_org
|
||||
@ -859,8 +859,8 @@
|
||||
email: user24@example.com
|
||||
keep_email_private: true
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user24
|
||||
@ -896,8 +896,8 @@
|
||||
email: org25@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: org25
|
||||
@ -933,8 +933,8 @@
|
||||
email: org26@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: onmention
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: org26
|
||||
@ -970,8 +970,8 @@
|
||||
email: user27@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user27
|
||||
@ -1007,8 +1007,8 @@
|
||||
email: user28@example.com
|
||||
keep_email_private: true
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user28
|
||||
@ -1044,8 +1044,8 @@
|
||||
email: user29@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user29
|
||||
@ -1081,8 +1081,8 @@
|
||||
email: user30@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user30
|
||||
@ -1118,8 +1118,8 @@
|
||||
email: user31@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user31
|
||||
@ -1155,7 +1155,7 @@
|
||||
email: user32@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f47017
|
||||
passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
@ -1192,8 +1192,8 @@
|
||||
email: user33@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: e82bc8ae42a53b98c3bd0f941aacc4aa2a264407534b0a11bf270137f67af912f694b67951f92148c45f91717e1478ca7889
|
||||
passwd_hash_algo: pbkdf2$50000$50
|
||||
passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
|
||||
passwd_hash_algo: argon2
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: user33
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestFixturesAreConsistent(t *testing.T) {
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
// TestFixturesAreConsistent assert that test fixtures are consistent
|
||||
|
@ -149,13 +149,13 @@ func MainTest(m *testing.M) {
|
||||
setting.AppDataPath = tmpDataPath
|
||||
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.InitProviderAndLoadCommonSettingsForTest()
|
||||
setting.LoadForTest()
|
||||
if err = git.InitFull(context.Background()); err != nil {
|
||||
fmt.Printf("Unable to InitFull: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
setting.LoadDBSetting()
|
||||
setting.InitLogs(true)
|
||||
setting.InitDBConfig()
|
||||
setting.NewLogServices(true)
|
||||
|
||||
exitStatus := m.Run()
|
||||
|
||||
|
@ -461,8 +461,6 @@ var migrations = []Migration{
|
||||
NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
|
||||
// v243 -> v244
|
||||
NewMigration("Add exclusive label", v1_19.AddExclusiveLabel),
|
||||
|
||||
// Gitea 1.19.0 ends at v244
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
@ -111,8 +111,12 @@ func (t *Team) ColorFormat(s fmt.State) {
|
||||
t.AccessMode)
|
||||
}
|
||||
|
||||
// LoadUnits load a list of available units for a team
|
||||
func (t *Team) LoadUnits(ctx context.Context) (err error) {
|
||||
// GetUnits return a list of available units for a team
|
||||
func (t *Team) GetUnits() error {
|
||||
return t.getUnits(db.DefaultContext)
|
||||
}
|
||||
|
||||
func (t *Team) getUnits(ctx context.Context) (err error) {
|
||||
if t.Units != nil {
|
||||
return nil
|
||||
}
|
||||
@ -189,7 +193,7 @@ func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool {
|
||||
|
||||
// UnitAccessMode returns if the team has the given unit type enabled
|
||||
func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
|
||||
if err := t.LoadUnits(ctx); err != nil {
|
||||
if err := t.getUnits(ctx); err != nil {
|
||||
log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ type TeamList []*Team
|
||||
|
||||
func (t TeamList) LoadUnits(ctx context.Context) error {
|
||||
for _, team := range t {
|
||||
if err := team.LoadUnits(ctx); err != nil {
|
||||
if err := team.getUnits(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
@ -19,7 +21,6 @@ import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/auth/openid"
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@ -29,6 +30,10 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
@ -43,6 +48,21 @@ const (
|
||||
UserTypeOrganization
|
||||
)
|
||||
|
||||
const (
|
||||
algoBcrypt = "bcrypt"
|
||||
algoScrypt = "scrypt"
|
||||
algoArgon2 = "argon2"
|
||||
algoPbkdf2 = "pbkdf2"
|
||||
)
|
||||
|
||||
// AvailableHashAlgorithms represents the available password hashing algorithms
|
||||
var AvailableHashAlgorithms = []string{
|
||||
algoPbkdf2,
|
||||
algoArgon2,
|
||||
algoScrypt,
|
||||
algoBcrypt,
|
||||
}
|
||||
|
||||
const (
|
||||
// EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own
|
||||
EmailNotificationsEnabled = "enabled"
|
||||
@ -357,6 +377,42 @@ func (u *User) NewGitSig() *git.Signature {
|
||||
}
|
||||
}
|
||||
|
||||
func hashPassword(passwd, salt, algo string) (string, error) {
|
||||
var tempPasswd []byte
|
||||
var saltBytes []byte
|
||||
|
||||
// There are two formats for the Salt value:
|
||||
// * The new format is a (32+)-byte hex-encoded string
|
||||
// * The old format was a 10-byte binary format
|
||||
// We have to tolerate both here but Authenticate should
|
||||
// regenerate the Salt following a successful validation.
|
||||
if len(salt) == 10 {
|
||||
saltBytes = []byte(salt)
|
||||
} else {
|
||||
var err error
|
||||
saltBytes, err = hex.DecodeString(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
switch algo {
|
||||
case algoBcrypt:
|
||||
tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
|
||||
return string(tempPasswd), nil
|
||||
case algoScrypt:
|
||||
tempPasswd, _ = scrypt.Key([]byte(passwd), saltBytes, 65536, 16, 2, 50)
|
||||
case algoArgon2:
|
||||
tempPasswd = argon2.IDKey([]byte(passwd), saltBytes, 2, 65536, 8, 50)
|
||||
case algoPbkdf2:
|
||||
fallthrough
|
||||
default:
|
||||
tempPasswd = pbkdf2.Key([]byte(passwd), saltBytes, 10000, 50, sha256.New)
|
||||
}
|
||||
|
||||
return hex.EncodeToString(tempPasswd), nil
|
||||
}
|
||||
|
||||
// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO
|
||||
// change passwd, salt and passwd_hash_algo fields
|
||||
func (u *User) SetPassword(passwd string) (err error) {
|
||||
@ -370,7 +426,7 @@ func (u *User) SetPassword(passwd string) (err error) {
|
||||
if u.Salt, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil {
|
||||
if u.Passwd, err = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo); err != nil {
|
||||
return err
|
||||
}
|
||||
u.PasswdHashAlgo = setting.PasswordHashAlgo
|
||||
@ -378,9 +434,20 @@ func (u *User) SetPassword(passwd string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePassword checks if the given password matches the one belonging to the user.
|
||||
// ValidatePassword checks if given password matches the one belongs to the user.
|
||||
func (u *User) ValidatePassword(passwd string) bool {
|
||||
return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt)
|
||||
tempHash, err := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 {
|
||||
return true
|
||||
}
|
||||
if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPasswordSet checks if the password is set or left empty
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@ -165,7 +164,7 @@ func TestEmailNotificationPreferences(t *testing.T) {
|
||||
func TestHashPasswordDeterministic(t *testing.T) {
|
||||
b := make([]byte, 16)
|
||||
u := &user_model.User{}
|
||||
algos := hash.RecommendedHashAlgorithms
|
||||
algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"}
|
||||
for j := 0; j < len(algos); j++ {
|
||||
u.PasswdHashAlgo = algos[j]
|
||||
for i := 0; i < 50; i++ {
|
||||
|
@ -1,80 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("argon2", NewArgon2Hasher)
|
||||
}
|
||||
|
||||
// Argon2Hasher implements PasswordHasher
|
||||
// and uses the Argon2 key derivation function, hybrant variant
|
||||
type Argon2Hasher struct {
|
||||
time uint32
|
||||
memory uint32
|
||||
threads uint8
|
||||
keyLen uint32
|
||||
}
|
||||
|
||||
// HashWithSaltBytes a provided password and salt
|
||||
func (hasher *Argon2Hasher) HashWithSaltBytes(password string, salt []byte) string {
|
||||
if hasher == nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(argon2.IDKey([]byte(password), salt, hasher.time, hasher.memory, hasher.threads, hasher.keyLen))
|
||||
}
|
||||
|
||||
// NewArgon2Hasher is a factory method to create an Argon2Hasher
|
||||
// The provided config should be either empty or of the form:
|
||||
// "<time>$<memory>$<threads>$<keyLen>", where <x> is the string representation
|
||||
// of an integer
|
||||
func NewArgon2Hasher(config string) *Argon2Hasher {
|
||||
// This default configuration uses the following parameters:
|
||||
// time=2, memory=64*1024, threads=8, keyLen=50.
|
||||
// It will make two passes through the memory, using 64MiB in total.
|
||||
// This matches the original configuration for `argon2` prior to storing hash parameters
|
||||
// in the database.
|
||||
// THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK
|
||||
hasher := &Argon2Hasher{
|
||||
time: 2,
|
||||
memory: 1 << 16,
|
||||
threads: 8,
|
||||
keyLen: 50,
|
||||
}
|
||||
|
||||
if config == "" {
|
||||
return hasher
|
||||
}
|
||||
|
||||
vals := strings.SplitN(config, "$", 4)
|
||||
if len(vals) != 4 {
|
||||
log.Error("invalid argon2 hash spec %s", config)
|
||||
return nil
|
||||
}
|
||||
|
||||
parsed, err := parseUIntParam(vals[0], "time", "argon2", config, nil)
|
||||
hasher.time = uint32(parsed)
|
||||
|
||||
parsed, err = parseUIntParam(vals[1], "memory", "argon2", config, err)
|
||||
hasher.memory = uint32(parsed)
|
||||
|
||||
parsed, err = parseUIntParam(vals[2], "threads", "argon2", config, err)
|
||||
hasher.threads = uint8(parsed)
|
||||
|
||||
parsed, err = parseUIntParam(vals[3], "keyLen", "argon2", config, err)
|
||||
hasher.keyLen = uint32(parsed)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return hasher
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("bcrypt", NewBcryptHasher)
|
||||
}
|
||||
|
||||
// BcryptHasher implements PasswordHasher
|
||||
// and uses the bcrypt password hash function.
|
||||
type BcryptHasher struct {
|
||||
cost int
|
||||
}
|
||||
|
||||
// HashWithSaltBytes a provided password and salt
|
||||
func (hasher *BcryptHasher) HashWithSaltBytes(password string, salt []byte) string {
|
||||
if hasher == nil {
|
||||
return ""
|
||||
}
|
||||
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), hasher.cost)
|
||||
return string(hashedPassword)
|
||||
}
|
||||
|
||||
func (hasher *BcryptHasher) VerifyPassword(password, hashedPassword, salt string) bool {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) == nil
|
||||
}
|
||||
|
||||
// NewBcryptHasher is a factory method to create an BcryptHasher
|
||||
// The provided config should be either empty or the string representation of the "<cost>"
|
||||
// as an integer
|
||||
func NewBcryptHasher(config string) *BcryptHasher {
|
||||
// This matches the original configuration for `bcrypt` prior to storing hash parameters
|
||||
// in the database.
|
||||
// THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK
|
||||
hasher := &BcryptHasher{
|
||||
cost: 10, // cost=10. i.e. 2^10 rounds of key expansion.
|
||||
}
|
||||
|
||||
if config == "" {
|
||||
return hasher
|
||||
}
|
||||
var err error
|
||||
hasher.cost, err = parseIntParam(config, "cost", "bcrypt", config, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return hasher
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
func parseIntParam(value, param, algorithmName, config string, previousErr error) (int, error) {
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config)
|
||||
return 0, err
|
||||
}
|
||||
return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed
|
||||
}
|
||||
|
||||
func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) {
|
||||
parsed, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config)
|
||||
return 0, err
|
||||
}
|
||||
return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// This package takes care of hashing passwords, verifying passwords, defining
|
||||
// available password algorithms, defining recommended password algorithms and
|
||||
// choosing the default password algorithm.
|
||||
|
||||
// PasswordSaltHasher will hash a provided password with the provided saltBytes
|
||||
type PasswordSaltHasher interface {
|
||||
HashWithSaltBytes(password string, saltBytes []byte) string
|
||||
}
|
||||
|
||||
// PasswordHasher will hash a provided password with the salt
|
||||
type PasswordHasher interface {
|
||||
Hash(password, salt string) (string, error)
|
||||
}
|
||||
|
||||
// PasswordVerifier will ensure that a providedPassword matches the hashPassword when hashed with the salt
|
||||
type PasswordVerifier interface {
|
||||
VerifyPassword(providedPassword, hashedPassword, salt string) bool
|
||||
}
|
||||
|
||||
// PasswordHashAlgorithms are named PasswordSaltHashers with a default verifier and hash function
|
||||
type PasswordHashAlgorithm struct {
|
||||
PasswordSaltHasher
|
||||
Specification string // The specification that is used to create the internal PasswordSaltHasher
|
||||
}
|
||||
|
||||
// Hash the provided password with the salt and return the hash
|
||||
func (algorithm *PasswordHashAlgorithm) Hash(password, salt string) (string, error) {
|
||||
var saltBytes []byte
|
||||
|
||||
// There are two formats for the salt value:
|
||||
// * The new format is a (32+)-byte hex-encoded string
|
||||
// * The old format was a 10-byte binary format
|
||||
// We have to tolerate both here.
|
||||
if len(salt) == 10 {
|
||||
saltBytes = []byte(salt)
|
||||
} else {
|
||||
var err error
|
||||
saltBytes, err = hex.DecodeString(salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return algorithm.HashWithSaltBytes(password, saltBytes), nil
|
||||
}
|
||||
|
||||
// Verify the provided password matches the hashPassword when hashed with the salt
|
||||
func (algorithm *PasswordHashAlgorithm) VerifyPassword(providedPassword, hashedPassword, salt string) bool {
|
||||
// Some PasswordSaltHashers have their own specialised compare function that takes into
|
||||
// account the stored parameters within the hash. e.g. bcrypt
|
||||
if verifier, ok := algorithm.PasswordSaltHasher.(PasswordVerifier); ok {
|
||||
return verifier.VerifyPassword(providedPassword, hashedPassword, salt)
|
||||
}
|
||||
|
||||
// Compute the hash of the password.
|
||||
providedPasswordHash, err := algorithm.Hash(providedPassword, salt)
|
||||
if err != nil {
|
||||
log.Error("passwordhash: %v.Hash(): %v", algorithm.Specification, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare it against the hashed password in constant-time.
|
||||
return subtle.ConstantTimeCompare([]byte(hashedPassword), []byte(providedPasswordHash)) == 1
|
||||
}
|
||||
|
||||
var (
|
||||
lastNonDefaultAlgorithm atomic.Value
|
||||
availableHasherFactories = map[string]func(string) PasswordSaltHasher{}
|
||||
)
|
||||
|
||||
// Register registers a PasswordSaltHasher with the availableHasherFactories
|
||||
// Caution: This is not thread safe.
|
||||
func Register[T PasswordSaltHasher](name string, newFn func(config string) T) {
|
||||
if _, has := availableHasherFactories[name]; has {
|
||||
panic(fmt.Errorf("duplicate registration of password salt hasher: %s", name))
|
||||
}
|
||||
|
||||
availableHasherFactories[name] = func(config string) PasswordSaltHasher {
|
||||
n := newFn(config)
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// In early versions of gitea the password hash algorithm field of a user could be
|
||||
// empty. At that point the default was `pbkdf2` without configuration values
|
||||
//
|
||||
// Please note this is not the same as the DefaultAlgorithm which is used
|
||||
// to determine what an empty PASSWORD_HASH_ALGO setting in the app.ini means.
|
||||
// These are not the same even if they have the same apparent value and they mean different things.
|
||||
//
|
||||
// DO NOT COALESCE THESE VALUES
|
||||
const defaultEmptyHashAlgorithmSpecification = "pbkdf2"
|
||||
|
||||
// Parse will convert the provided algorithm specification in to a PasswordHashAlgorithm
|
||||
// If the provided specification matches the DefaultHashAlgorithm Specification it will be
|
||||
// used.
|
||||
// In addition the last non-default hasher will be cached to help reduce the load from
|
||||
// parsing specifications.
|
||||
//
|
||||
// NOTE: No de-aliasing is done in this function, thus any specification which does not
|
||||
// contain a configuration will use the default values for that hasher. These are not
|
||||
// necessarily the same values as those obtained by dealiasing. This allows for
|
||||
// seamless backwards compatibility with the original configuration.
|
||||
//
|
||||
// To further labour this point, running `Parse("pbkdf2")` does not obtain the
|
||||
// same algorithm as setting `PASSWORD_HASH_ALGO=pbkdf2` in app.ini, nor is it intended to.
|
||||
// A user that has `password_hash_algo='pbkdf2'` in the db means get the original, unconfigured algorithm
|
||||
// Users will be migrated automatically as they log-in to have the complete specification stored
|
||||
// in their `password_hash_algo` fields by other code.
|
||||
func Parse(algorithmSpec string) *PasswordHashAlgorithm {
|
||||
if algorithmSpec == "" {
|
||||
algorithmSpec = defaultEmptyHashAlgorithmSpecification
|
||||
}
|
||||
|
||||
if DefaultHashAlgorithm != nil && algorithmSpec == DefaultHashAlgorithm.Specification {
|
||||
return DefaultHashAlgorithm
|
||||
}
|
||||
|
||||
ptr := lastNonDefaultAlgorithm.Load()
|
||||
if ptr != nil {
|
||||
hashAlgorithm, ok := ptr.(*PasswordHashAlgorithm)
|
||||
if ok && hashAlgorithm.Specification == algorithmSpec {
|
||||
return hashAlgorithm
|
||||
}
|
||||
}
|
||||
|
||||
// Now convert the provided specification in to a hasherType +/- some configuration parameters
|
||||
vals := strings.SplitN(algorithmSpec, "$", 2)
|
||||
var hasherType string
|
||||
var config string
|
||||
|
||||
if len(vals) == 0 {
|
||||
// This should not happen as algorithmSpec should not be empty
|
||||
// due to it being assigned to defaultEmptyHashAlgorithmSpecification above
|
||||
// but we should be absolutely cautious here
|
||||
return nil
|
||||
}
|
||||
|
||||
hasherType = vals[0]
|
||||
if len(vals) > 1 {
|
||||
config = vals[1]
|
||||
}
|
||||
|
||||
newFn, has := availableHasherFactories[hasherType]
|
||||
if !has {
|
||||
// unknown hasher type
|
||||
return nil
|
||||
}
|
||||
|
||||
ph := newFn(config)
|
||||
if ph == nil {
|
||||
// The provided configuration is likely invalid - it will have been logged already
|
||||
// but we cannot hash safely
|
||||
return nil
|
||||
}
|
||||
|
||||
hashAlgorithm := &PasswordHashAlgorithm{
|
||||
PasswordSaltHasher: ph,
|
||||
Specification: algorithmSpec,
|
||||
}
|
||||
|
||||
lastNonDefaultAlgorithm.Store(hashAlgorithm)
|
||||
|
||||
return hashAlgorithm
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testSaltHasher string
|
||||
|
||||
func (t testSaltHasher) HashWithSaltBytes(password string, salt []byte) string {
|
||||
return password + "$" + string(salt) + "$" + string(t)
|
||||
}
|
||||
|
||||
func Test_registerHasher(t *testing.T) {
|
||||
Register("Test_registerHasher", func(config string) testSaltHasher {
|
||||
return testSaltHasher(config)
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
Register("Test_registerHasher", func(config string) testSaltHasher {
|
||||
return testSaltHasher(config)
|
||||
})
|
||||
})
|
||||
|
||||
assert.Equal(t, "password$salt$",
|
||||
Parse("Test_registerHasher").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt")))
|
||||
|
||||
assert.Equal(t, "password$salt$config",
|
||||
Parse("Test_registerHasher$config").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt")))
|
||||
|
||||
delete(availableHasherFactories, "Test_registerHasher")
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
hashAlgorithmsToTest := []string{}
|
||||
for plainHashAlgorithmNames := range availableHasherFactories {
|
||||
hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames)
|
||||
}
|
||||
for _, aliased := range aliasAlgorithmNames {
|
||||
if strings.Contains(aliased, "$") {
|
||||
hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased)
|
||||
}
|
||||
}
|
||||
for _, algorithmName := range hashAlgorithmsToTest {
|
||||
t.Run(algorithmName, func(t *testing.T) {
|
||||
algo := Parse(algorithmName)
|
||||
assert.NotNil(t, algo, "Algorithm %s resulted in an empty algorithm", algorithmName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashing(t *testing.T) {
|
||||
hashAlgorithmsToTest := []string{}
|
||||
for plainHashAlgorithmNames := range availableHasherFactories {
|
||||
hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames)
|
||||
}
|
||||
for _, aliased := range aliasAlgorithmNames {
|
||||
if strings.Contains(aliased, "$") {
|
||||
hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased)
|
||||
}
|
||||
}
|
||||
|
||||
runTests := func(password, salt string, shouldPass bool) {
|
||||
for _, algorithmName := range hashAlgorithmsToTest {
|
||||
t.Run(algorithmName, func(t *testing.T) {
|
||||
output, err := Parse(algorithmName).Hash(password, salt)
|
||||
if shouldPass {
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, output, "output for %s was empty", algorithmName)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, Parse(algorithmName).VerifyPassword(password, output, salt), shouldPass)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test with new salt format.
|
||||
runTests(strings.Repeat("a", 16), hex.EncodeToString([]byte{0x01, 0x02, 0x03}), true)
|
||||
|
||||
// Test with legacy salt format.
|
||||
runTests(strings.Repeat("a", 16), strings.Repeat("b", 10), true)
|
||||
|
||||
// Test with invalid salt.
|
||||
runTests(strings.Repeat("a", 16), "a", false)
|
||||
}
|
||||
|
||||
// vectors were generated using the current codebase.
|
||||
var vectors = []struct {
|
||||
algorithms []string
|
||||
password string
|
||||
salt string
|
||||
output string
|
||||
shouldfail bool
|
||||
}{
|
||||
{
|
||||
algorithms: []string{"bcrypt", "bcrypt$10"},
|
||||
password: "abcdef",
|
||||
salt: strings.Repeat("a", 10),
|
||||
output: "$2a$10$fjtm8BsQ2crym01/piJroenO3oSVUBhSLKaGdTYJ4tG0ePVCrU0G2",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"scrypt", "scrypt$65536$16$2$50"},
|
||||
password: "abcdef",
|
||||
salt: strings.Repeat("a", 10),
|
||||
output: "3b571d0c07c62d42b7bad3dbf18fb0cd67d4d8cd4ad4c6928e1090e5b2a4a84437c6fd2627d897c0e7e65025ca62b67a0002",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"argon2", "argon2$2$65536$8$50"},
|
||||
password: "abcdef",
|
||||
salt: strings.Repeat("a", 10),
|
||||
output: "551f089f570f989975b6f7c6a8ff3cf89bc486dd7bbe87ed4d80ad4362f8ee599ec8dda78dac196301b98456402bcda775dc",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"pbkdf2", "pbkdf2$10000$50"},
|
||||
password: "abcdef",
|
||||
salt: strings.Repeat("a", 10),
|
||||
output: "ab48d5471b7e6ed42d10001db88c852ff7303c788e49da5c3c7b63d5adf96360303724b74b679223a3dea8a242d10abb1913",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"bcrypt", "bcrypt$10"},
|
||||
password: "abcdef",
|
||||
salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}),
|
||||
output: "$2a$10$qhgm32w9ZpqLygugWJsLjey8xRGcaq9iXAfmCeNBXxddgyoaOC3Gq",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"scrypt", "scrypt$65536$16$2$50"},
|
||||
password: "abcdef",
|
||||
salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}),
|
||||
output: "25fe5f66b43fa4eb7b6717905317cd2223cf841092dc8e0a1e8c75720ad4846cb5d9387303e14bc3c69faa3b1c51ef4b7de1",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"argon2", "argon2$2$65536$8$50"},
|
||||
password: "abcdef",
|
||||
salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}),
|
||||
output: "9c287db63a91d18bb1414b703216da4fc431387c1ae7c8acdb280222f11f0929831055dbfd5126a3b48566692e83ec750d2a",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"pbkdf2", "pbkdf2$10000$50"},
|
||||
password: "abcdef",
|
||||
salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}),
|
||||
output: "45d6cdc843d65cf0eda7b90ab41435762a282f7df013477a1c5b212ba81dbdca2edf1ecc4b5cb05956bb9e0c37ab29315d78",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"pbkdf2$320000$50"},
|
||||
password: "abcdef",
|
||||
salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}),
|
||||
output: "84e233114499e8721da80e85568e5b7b5900b3e49a30845fcda9d1e1756da4547d70f8740ac2b4a5d82f88cebcd27f21bfe2",
|
||||
shouldfail: false,
|
||||
},
|
||||
{
|
||||
algorithms: []string{"pbkdf2", "pbkdf2$10000$50"},
|
||||
password: "abcdef",
|
||||
salt: "",
|
||||
output: "",
|
||||
shouldfail: true,
|
||||
},
|
||||
}
|
||||
|
||||
// Ensure that the current code will correctly verify against the test vectors.
|
||||
func TestVectors(t *testing.T) {
|
||||
for i, vector := range vectors {
|
||||
for _, algorithm := range vector.algorithms {
|
||||
t.Run(strconv.Itoa(i)+": "+algorithm, func(t *testing.T) {
|
||||
pa := Parse(algorithm)
|
||||
assert.Equal(t, !vector.shouldfail, pa.VerifyPassword(vector.password, vector.output, vector.salt))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("pbkdf2", NewPBKDF2Hasher)
|
||||
}
|
||||
|
||||
// PBKDF2Hasher implements PasswordHasher
|
||||
// and uses the PBKDF2 key derivation function.
|
||||
type PBKDF2Hasher struct {
|
||||
iter, keyLen int
|
||||
}
|
||||
|
||||
// HashWithSaltBytes a provided password and salt
|
||||
func (hasher *PBKDF2Hasher) HashWithSaltBytes(password string, salt []byte) string {
|
||||
if hasher == nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(pbkdf2.Key([]byte(password), salt, hasher.iter, hasher.keyLen, sha256.New))
|
||||
}
|
||||
|
||||
// NewPBKDF2Hasher is a factory method to create an PBKDF2Hasher
|
||||
// config should be either empty or of the form:
|
||||
// "<iter>$<keyLen>", where <x> is the string representation
|
||||
// of an integer
|
||||
func NewPBKDF2Hasher(config string) *PBKDF2Hasher {
|
||||
// This default configuration uses the following parameters:
|
||||
// iter=10000, keyLen=50.
|
||||
// This matches the original configuration for `pbkdf2` prior to storing parameters
|
||||
// in the database.
|
||||
// THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK
|
||||
hasher := &PBKDF2Hasher{
|
||||
iter: 10_000,
|
||||
keyLen: 50,
|
||||
}
|
||||
|
||||
if config == "" {
|
||||
return hasher
|
||||
}
|
||||
|
||||
vals := strings.SplitN(config, "$", 2)
|
||||
if len(vals) != 2 {
|
||||
log.Error("invalid pbkdf2 hash spec %s", config)
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
hasher.iter, err = parseIntParam(vals[0], "iter", "pbkdf2", config, nil)
|
||||
hasher.keyLen, err = parseIntParam(vals[1], "keyLen", "pbkdf2", config, err)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return hasher
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("scrypt", NewScryptHasher)
|
||||
}
|
||||
|
||||
// ScryptHasher implements PasswordHasher
|
||||
// and uses the scrypt key derivation function.
|
||||
type ScryptHasher struct {
|
||||
n, r, p, keyLen int
|
||||
}
|
||||
|
||||
// HashWithSaltBytes a provided password and salt
|
||||
func (hasher *ScryptHasher) HashWithSaltBytes(password string, salt []byte) string {
|
||||
if hasher == nil {
|
||||
return ""
|
||||
}
|
||||
hashedPassword, _ := scrypt.Key([]byte(password), salt, hasher.n, hasher.r, hasher.p, hasher.keyLen)
|
||||
return hex.EncodeToString(hashedPassword)
|
||||
}
|
||||
|
||||
// NewScryptHasher is a factory method to create an ScryptHasher
|
||||
// The provided config should be either empty or of the form:
|
||||
// "<n>$<r>$<p>$<keyLen>", where <x> is the string representation
|
||||
// of an integer
|
||||
func NewScryptHasher(config string) *ScryptHasher {
|
||||
// This matches the original configuration for `scrypt` prior to storing hash parameters
|
||||
// in the database.
|
||||
// THESE VALUES MUST NOT BE CHANGED OR BACKWARDS COMPATIBILITY WILL BREAK
|
||||
hasher := &ScryptHasher{
|
||||
n: 1 << 16,
|
||||
r: 16,
|
||||
p: 2, // 2 passes through memory - this default config will use 128MiB in total.
|
||||
keyLen: 50,
|
||||
}
|
||||
|
||||
if config == "" {
|
||||
return hasher
|
||||
}
|
||||
|
||||
vals := strings.SplitN(config, "$", 4)
|
||||
if len(vals) != 4 {
|
||||
log.Error("invalid scrypt hash spec %s", config)
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
hasher.n, err = parseIntParam(vals[0], "n", "scrypt", config, nil)
|
||||
hasher.r, err = parseIntParam(vals[1], "r", "scrypt", config, err)
|
||||
hasher.p, err = parseIntParam(vals[2], "p", "scrypt", config, err)
|
||||
hasher.keyLen, err = parseIntParam(vals[3], "keyLen", "scrypt", config, err)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return hasher
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
// DefaultHashAlgorithmName represents the default value of PASSWORD_HASH_ALGO
|
||||
// configured in app.ini.
|
||||
//
|
||||
// It is NOT the same and does NOT map to the defaultEmptyHashAlgorithmSpecification.
|
||||
//
|
||||
// It will be dealiased as per aliasAlgorithmNames whereas
|
||||
// defaultEmptyHashAlgorithmSpecification does not undergo dealiasing.
|
||||
const DefaultHashAlgorithmName = "pbkdf2"
|
||||
|
||||
var DefaultHashAlgorithm *PasswordHashAlgorithm
|
||||
|
||||
// aliasAlgorithNames provides a mapping between the value of PASSWORD_HASH_ALGO
|
||||
// configured in the app.ini and the parameters used within the hashers internally.
|
||||
//
|
||||
// If it is necessary to change the default parameters for any hasher in future you
|
||||
// should change these values and not those in argon2.go etc.
|
||||
var aliasAlgorithmNames = map[string]string{
|
||||
"argon2": "argon2$2$65536$8$50",
|
||||
"bcrypt": "bcrypt$10",
|
||||
"scrypt": "scrypt$65536$16$2$50",
|
||||
"pbkdf2": "pbkdf2_v2", // pbkdf2 should default to pbkdf2_v2
|
||||
"pbkdf2_v1": "pbkdf2$10000$50",
|
||||
// The latest PBKDF2 password algorithm is used as the default since it doesn't
|
||||
// use a lot of memory and is safer to use on less powerful devices.
|
||||
"pbkdf2_v2": "pbkdf2$50000$50",
|
||||
// The pbkdf2_hi password algorithm is offered as a stronger alternative to the
|
||||
// slightly improved pbkdf2_v2 algorithm
|
||||
"pbkdf2_hi": "pbkdf2$320000$50",
|
||||
}
|
||||
|
||||
var RecommendedHashAlgorithms = []string{
|
||||
"pbkdf2",
|
||||
"argon2",
|
||||
"bcrypt",
|
||||
"scrypt",
|
||||
"pbkdf2_hi",
|
||||
}
|
||||
|
||||
// SetDefaultPasswordHashAlgorithm will take a provided algorithmName and dealias it to
|
||||
// a complete algorithm specification.
|
||||
func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) {
|
||||
if algorithmName == "" {
|
||||
algorithmName = DefaultHashAlgorithmName
|
||||
}
|
||||
alias, has := aliasAlgorithmNames[algorithmName]
|
||||
for has {
|
||||
algorithmName = alias
|
||||
alias, has = aliasAlgorithmNames[algorithmName]
|
||||
}
|
||||
|
||||
// algorithmName should now be a full algorithm specification
|
||||
// e.g. pbkdf2$50000$50 rather than pbdkf2
|
||||
DefaultHashAlgorithm = Parse(algorithmName)
|
||||
|
||||
return algorithmName, DefaultHashAlgorithm
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckSettingPasswordHashAlgorithm(t *testing.T) {
|
||||
t.Run("pbkdf2 is pbkdf2_v2", func(t *testing.T) {
|
||||
pbkdf2v2Config, pbkdf2v2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2_v2")
|
||||
pbkdf2Config, pbkdf2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2")
|
||||
|
||||
assert.Equal(t, pbkdf2v2Config, pbkdf2Config)
|
||||
assert.Equal(t, pbkdf2v2Algo.Specification, pbkdf2Algo.Specification)
|
||||
})
|
||||
|
||||
for a, b := range aliasAlgorithmNames {
|
||||
t.Run(a+"="+b, func(t *testing.T) {
|
||||
aConfig, aAlgo := SetDefaultPasswordHashAlgorithm(a)
|
||||
bConfig, bAlgo := SetDefaultPasswordHashAlgorithm(b)
|
||||
|
||||
assert.Equal(t, bConfig, aConfig)
|
||||
assert.Equal(t, aAlgo.Specification, bAlgo.Specification)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("pbkdf2_v2 is the default when default password hash algorithm is empty", func(t *testing.T) {
|
||||
emptyConfig, emptyAlgo := SetDefaultPasswordHashAlgorithm("")
|
||||
pbkdf2v2Config, pbkdf2v2Algo := SetDefaultPasswordHashAlgorithm("pbkdf2_v2")
|
||||
|
||||
assert.Equal(t, pbkdf2v2Config, emptyConfig)
|
||||
assert.Equal(t, pbkdf2v2Algo.Specification, emptyAlgo.Specification)
|
||||
})
|
||||
}
|
@ -27,7 +27,7 @@ var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey
|
||||
// AccessLogger returns a middleware to log access logger
|
||||
func AccessLogger() func(http.Handler) http.Handler {
|
||||
logger := log.GetLogger("access")
|
||||
logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
|
||||
logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate)
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
start := time.Now()
|
||||
|
@ -44,10 +44,10 @@ func (w *wrappedLevelLogger) Log(skip int, level log.Level, format string, v ...
|
||||
}
|
||||
|
||||
func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadDBSetting()
|
||||
setting.InitSQLLog(disableConsole)
|
||||
setting.LoadFromExisting()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.NewXORMLogService(disableConsole)
|
||||
if err := db.InitEngine(ctx); err != nil {
|
||||
return fmt.Errorf("db.InitEngine: %w", err)
|
||||
}
|
||||
@ -71,7 +71,7 @@ func RunChecks(ctx context.Context, logger log.Logger, autofix bool, checks []*C
|
||||
for i, check := range checks {
|
||||
if !dbIsInit && !check.SkipDatabaseInitialization {
|
||||
// Only open database after the most basic configuration check
|
||||
setting.Log.EnableXORMLog = false
|
||||
setting.EnableXORMLog = false
|
||||
if err := initDBDisableConsole(ctx, true); err != nil {
|
||||
logger.Error("Error whilst initializing the database: %v", err)
|
||||
logger.Error("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
|
@ -67,8 +67,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
|
||||
return err
|
||||
}
|
||||
|
||||
setting.InitProviderFromExistingFile()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadFromExisting()
|
||||
|
||||
configurationFiles := []configurationFile{
|
||||
{"Configuration File Path", setting.CustomConf, false, true, false},
|
||||
@ -76,7 +75,7 @@ func checkConfigurationFiles(ctx context.Context, logger log.Logger, autofix boo
|
||||
{"Data Root Path", setting.AppDataPath, true, true, true},
|
||||
{"Custom File Root Path", setting.CustomPath, true, false, false},
|
||||
{"Work directory", setting.AppWorkPath, true, true, false},
|
||||
{"Log Root Path", setting.Log.RootPath, true, true, true},
|
||||
{"Log Root Path", setting.LogRootPath, true, true, true},
|
||||
}
|
||||
|
||||
if options.IsDynamic() {
|
||||
|
@ -41,8 +41,12 @@ var (
|
||||
// NewContext loads custom highlight map from local config
|
||||
func NewContext() {
|
||||
once.Do(func() {
|
||||
highlightMapping = setting.GetHighlightMapping()
|
||||
|
||||
if setting.Cfg != nil {
|
||||
keys := setting.Cfg.Section("highlight.mapping").Keys()
|
||||
for i := range keys {
|
||||
highlightMapping[keys[i].Name()] = keys[i].Value()
|
||||
}
|
||||
}
|
||||
// The size 512 is simply a conservative rule of thumb
|
||||
c, err := lru.New2Q(512)
|
||||
if err != nil {
|
||||
|
@ -27,11 +27,11 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestBleveSearchIssues(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.CfgProvider = ini.Empty()
|
||||
setting.Cfg = ini.Empty()
|
||||
|
||||
tmpIndexerDir := t.TempDir()
|
||||
|
||||
setting.CfgProvider.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue"))
|
||||
setting.Cfg.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue"))
|
||||
|
||||
oldIssuePath := setting.Indexer.IssuePath
|
||||
setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue")
|
||||
@ -40,7 +40,7 @@ func TestBleveSearchIssues(t *testing.T) {
|
||||
}()
|
||||
|
||||
setting.Indexer.IssueType = "bleve"
|
||||
setting.LoadQueueSettings()
|
||||
setting.NewQueueService()
|
||||
InitIssueIndexer(true)
|
||||
defer func() {
|
||||
indexer := holder.get()
|
||||
|
@ -29,9 +29,9 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestRepoStatsIndex(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.CfgProvider = ini.Empty()
|
||||
setting.Cfg = ini.Empty()
|
||||
|
||||
setting.LoadQueueSettings()
|
||||
setting.NewQueueService()
|
||||
|
||||
err := Init()
|
||||
assert.NoError(t, err)
|
||||
|
@ -28,8 +28,7 @@ var localMetas = map[string]string{
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setting.InitProviderAllowEmpty()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadAllowEmpty()
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
log.Fatal("git init failed, err: %v", err)
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ var localMetas = map[string]string{
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setting.InitProviderAllowEmpty()
|
||||
setting.LoadCommonSettings()
|
||||
setting.LoadAllowEmpty()
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
log.Fatal("git init failed, err: %v", err)
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
// complexity contains information about a particular kind of password complexity
|
||||
@ -112,13 +112,13 @@ func Generate(n int) (string, error) {
|
||||
}
|
||||
|
||||
// BuildComplexityError builds the error message when password complexity checks fail
|
||||
func BuildComplexityError(locale translation.Locale) string {
|
||||
func BuildComplexityError(ctx *context.Context) string {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(locale.Tr("form.password_complexity"))
|
||||
buffer.WriteString(ctx.Tr("form.password_complexity"))
|
||||
buffer.WriteString("<ul>")
|
||||
for _, c := range requiredList {
|
||||
buffer.WriteString("<li>")
|
||||
buffer.WriteString(locale.Tr(c.TrNameOne))
|
||||
buffer.WriteString(ctx.Tr(c.TrNameOne))
|
||||
buffer.WriteString("</li>")
|
||||
}
|
||||
buffer.WriteString("</ul>")
|
@ -6,7 +6,7 @@ package password
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/modules/auth/password/pwn"
|
||||
"code.gitea.io/gitea/modules/password/pwn"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
@ -19,11 +19,11 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func loadActionsFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("actions")
|
||||
func newActions() {
|
||||
sec := Cfg.Section("actions")
|
||||
if err := sec.MapTo(&Actions); err != nil {
|
||||
log.Fatal("Failed to map Actions settings: %v", err)
|
||||
}
|
||||
|
||||
Actions.Storage = getStorage(rootCfg, "actions_log", "", nil)
|
||||
Actions.Storage = getStorage("actions_log", "", nil)
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
// Admin settings
|
||||
var Admin struct {
|
||||
DisableRegularOrgCreation bool
|
||||
DefaultEmailNotification string
|
||||
}
|
||||
|
||||
func loadAdminFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "admin", &Admin)
|
||||
sec := rootCfg.Section("admin")
|
||||
Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled")
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// API settings
|
||||
var API = struct {
|
||||
EnableSwagger bool
|
||||
SwaggerURL string
|
||||
MaxResponseItems int
|
||||
DefaultPagingNum int
|
||||
DefaultGitTreesPerPage int
|
||||
DefaultMaxBlobSize int64
|
||||
}{
|
||||
EnableSwagger: true,
|
||||
SwaggerURL: "",
|
||||
MaxResponseItems: 50,
|
||||
DefaultPagingNum: 30,
|
||||
DefaultGitTreesPerPage: 1000,
|
||||
DefaultMaxBlobSize: 10485760,
|
||||
}
|
||||
|
||||
func loadAPIFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "api", &API)
|
||||
|
||||
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
|
||||
u, err := url.Parse(rootCfg.Section("server").Key("ROOT_URL").MustString(defaultAppURL))
|
||||
if err != nil {
|
||||
log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
|
||||
}
|
||||
u.Path = path.Join(u.Path, "api", "swagger")
|
||||
API.SwaggerURL = u.String()
|
||||
}
|
@ -20,11 +20,11 @@ var Attachment = struct {
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
func loadAttachmentFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("attachment")
|
||||
func newAttachmentService() {
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
|
||||
Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec)
|
||||
Attachment.Storage = getStorage("attachments", storageType, sec)
|
||||
|
||||
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
|
||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
|
||||
|
@ -49,8 +49,8 @@ var CacheService = struct {
|
||||
// MemcacheMaxTTL represents the maximum memcache TTL
|
||||
const MemcacheMaxTTL = 30 * 24 * time.Hour
|
||||
|
||||
func loadCacheFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("cache")
|
||||
func newCacheService() {
|
||||
sec := Cfg.Section("cache")
|
||||
if err := sec.MapTo(&CacheService); err != nil {
|
||||
log.Fatal("Failed to map Cache settings: %v", err)
|
||||
}
|
||||
@ -79,7 +79,7 @@ func loadCacheFrom(rootCfg ConfigProvider) {
|
||||
Service.EnableCaptcha = false
|
||||
}
|
||||
|
||||
sec = rootCfg.Section("cache.last_commit")
|
||||
sec = Cfg.Section("cache.last_commit")
|
||||
if !CacheService.Enabled {
|
||||
CacheService.LastCommit.Enabled = false
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import "code.gitea.io/gitea/modules/log"
|
||||
|
||||
var Camo = struct {
|
||||
Enabled bool
|
||||
ServerURL string `ini:"SERVER_URL"`
|
||||
HMACKey string `ini:"HMAC_KEY"`
|
||||
Allways bool
|
||||
}{}
|
||||
|
||||
func loadCamoFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "camo", &Camo)
|
||||
if Camo.Enabled {
|
||||
if Camo.ServerURL == "" || Camo.HMACKey == "" {
|
||||
log.Fatal(`Camo settings require "SERVER_URL" and HMAC_KEY`)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// ConfigProvider represents a config provider
|
||||
type ConfigProvider interface {
|
||||
Section(section string) *ini.Section
|
||||
NewSection(name string) (*ini.Section, error)
|
||||
GetSection(name string) (*ini.Section, error)
|
||||
}
|
||||
|
||||
// a file is an implementation ConfigProvider and other implementations are possible, i.e. from docker, k8s, …
|
||||
var _ ConfigProvider = &ini.File{}
|
||||
|
||||
func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting interface{}) {
|
||||
if err := rootCfg.Section(sectionName).MapTo(setting); err != nil {
|
||||
log.Fatal("Failed to map %s settings: %v", sectionName, err)
|
||||
}
|
||||
}
|
||||
|
||||
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be removed in v1.19.0", oldSection, oldKey, newSection, newKey)
|
||||
}
|
||||
}
|
||||
|
||||
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
|
||||
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
|
||||
}
|
||||
}
|
@ -27,8 +27,12 @@ var CORSConfig = struct {
|
||||
XFrameOptions: "SAMEORIGIN",
|
||||
}
|
||||
|
||||
func loadCorsFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "cors", &CORSConfig)
|
||||
func newCORSService() {
|
||||
sec := Cfg.Section("cors")
|
||||
if err := sec.MapTo(&CORSConfig); err != nil {
|
||||
log.Fatal("Failed to map cors settings: %v", err)
|
||||
}
|
||||
|
||||
if CORSConfig.Enabled {
|
||||
log.Info("CORS Service Enabled")
|
||||
}
|
||||
|
@ -7,11 +7,7 @@ import "reflect"
|
||||
|
||||
// GetCronSettings maps the cron subsection to the provided config
|
||||
func GetCronSettings(name string, config interface{}) (interface{}, error) {
|
||||
return getCronSettings(CfgProvider, name, config)
|
||||
}
|
||||
|
||||
func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) {
|
||||
if err := rootCfg.Section("cron." + name).MapTo(config); err != nil {
|
||||
if err := Cfg.Section("cron." + name).MapTo(config); err != nil {
|
||||
return config, err
|
||||
}
|
||||
|
||||
@ -22,7 +18,7 @@ func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (i
|
||||
field := val.Field(i)
|
||||
tpField := typ.Field(i)
|
||||
if tpField.Type.Kind() == reflect.Struct && tpField.Anonymous {
|
||||
if err := rootCfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil {
|
||||
if err := Cfg.Section("cron." + name).MapTo(field.Addr().Interface()); err != nil {
|
||||
return config, err
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func Test_getCronSettings(t *testing.T) {
|
||||
func Test_GetCronSettings(t *testing.T) {
|
||||
type BaseStruct struct {
|
||||
Base bool
|
||||
Second string
|
||||
@ -27,8 +27,7 @@ Base = true
|
||||
Second = white rabbit
|
||||
Extend = true
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
extended := &Extended{
|
||||
BaseStruct: BaseStruct{
|
||||
@ -36,7 +35,8 @@ Extend = true
|
||||
},
|
||||
}
|
||||
|
||||
_, err = getCronSettings(cfg, "test", extended)
|
||||
_, err := GetCronSettings("test", extended)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, extended.Base)
|
||||
assert.EqualValues(t, extended.Second, "white rabbit")
|
||||
|
@ -56,9 +56,9 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// LoadDBSetting loads the database settings
|
||||
func LoadDBSetting() {
|
||||
sec := CfgProvider.Section("database")
|
||||
// InitDBConfig loads the database settings
|
||||
func InitDBConfig() {
|
||||
sec := Cfg.Section("database")
|
||||
Database.Type = sec.Key("DB_TYPE").String()
|
||||
defaultCharset := "utf8"
|
||||
Database.UseMySQL = false
|
||||
|
39
modules/setting/directory.go
Normal file
39
modules/setting/directory.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// PrepareAppDataPath creates app data directory if necessary
|
||||
func PrepareAppDataPath() error {
|
||||
// FIXME: There are too many calls to MkdirAll in old code. It is incorrect.
|
||||
// For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs,
|
||||
// then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem.
|
||||
// The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories.
|
||||
// For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK).
|
||||
// Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future.
|
||||
|
||||
st, err := os.Stat(AppDataPath)
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(AppDataPath, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create the APP_DATA_PATH directory: %q, Error: %w", AppDataPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to use APP_DATA_PATH %q. Error: %w", AppDataPath, err)
|
||||
}
|
||||
|
||||
if !st.IsDir() /* also works for symlink */ {
|
||||
return fmt.Errorf("the APP_DATA_PATH %q is not a directory (or symlink to a directory) and can't be used", AppDataPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -33,8 +33,8 @@ var (
|
||||
// HttpsigAlgs is a constant slice of httpsig algorithm objects
|
||||
var HttpsigAlgs []httpsig.Algorithm
|
||||
|
||||
func loadFederationFrom(rootCfg ConfigProvider) {
|
||||
if err := rootCfg.Section("federation").MapTo(&Federation); err != nil {
|
||||
func newFederationService() {
|
||||
if err := Cfg.Section("federation").MapTo(&Federation); err != nil {
|
||||
log.Fatal("Failed to map Federation settings: %v", err)
|
||||
} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) {
|
||||
log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm)
|
||||
|
@ -67,8 +67,9 @@ var Git = struct {
|
||||
},
|
||||
}
|
||||
|
||||
func loadGitFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("git")
|
||||
func newGit() {
|
||||
sec := Cfg.Section("git")
|
||||
|
||||
if err := sec.MapTo(&Git); err != nil {
|
||||
log.Fatal("Failed to map Git settings: %v", err)
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
func GetHighlightMapping() map[string]string {
|
||||
highlightMapping := map[string]string{}
|
||||
if CfgProvider == nil {
|
||||
return highlightMapping
|
||||
}
|
||||
|
||||
keys := CfgProvider.Section("highlight.mapping").Keys()
|
||||
for _, key := range keys {
|
||||
highlightMapping[key.Name()] = key.Value()
|
||||
}
|
||||
return highlightMapping
|
||||
}
|
@ -47,20 +47,3 @@ func defaultI18nNames() (res []string) {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
var (
|
||||
// I18n settings
|
||||
Langs []string
|
||||
Names []string
|
||||
)
|
||||
|
||||
func loadI18nFrom(rootCfg ConfigProvider) {
|
||||
Langs = rootCfg.Section("i18n").Key("LANGS").Strings(",")
|
||||
if len(Langs) == 0 {
|
||||
Langs = defaultI18nLangs()
|
||||
}
|
||||
Names = rootCfg.Section("i18n").Key("NAMES").Strings(",")
|
||||
if len(Names) == 0 {
|
||||
Names = defaultI18nNames()
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,10 @@ var IncomingEmail = struct {
|
||||
MaximumMessageSize: 10485760,
|
||||
}
|
||||
|
||||
func loadIncomingEmailFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "email.incoming", &IncomingEmail)
|
||||
func newIncomingEmail() {
|
||||
if err := Cfg.Section("email.incoming").MapTo(&IncomingEmail); err != nil {
|
||||
log.Fatal("Unable to map [email.incoming] section on to IncomingEmail. Error: %v", err)
|
||||
}
|
||||
|
||||
if !IncomingEmail.Enabled {
|
||||
return
|
||||
|
@ -45,8 +45,8 @@ var Indexer = struct {
|
||||
ExcludeVendored: true,
|
||||
}
|
||||
|
||||
func loadIndexerFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("indexer")
|
||||
func newIndexerService() {
|
||||
sec := Cfg.Section("indexer")
|
||||
Indexer.IssueType = sec.Key("ISSUE_INDEXER_TYPE").MustString("bleve")
|
||||
Indexer.IssuePath = filepath.ToSlash(sec.Key("ISSUE_INDEXER_PATH").MustString(filepath.ToSlash(filepath.Join(AppDataPath, "indexers/issues.bleve"))))
|
||||
if !filepath.IsAbs(Indexer.IssuePath) {
|
||||
@ -57,11 +57,11 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
|
||||
|
||||
// The following settings are deprecated and can be overridden by settings in [queue] or [queue.issue_indexer]
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE")
|
||||
deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR")
|
||||
deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR")
|
||||
deprecatedSetting(rootCfg, "indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH")
|
||||
deprecatedSetting(rootCfg, "indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH")
|
||||
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_TYPE", "queue.issue_indexer", "TYPE")
|
||||
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_DIR", "queue.issue_indexer", "DATADIR")
|
||||
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_CONN_STR", "queue.issue_indexer", "CONN_STR")
|
||||
deprecatedSetting("indexer", "ISSUE_INDEXER_QUEUE_BATCH_NUMBER", "queue.issue_indexer", "BATCH_LENGTH")
|
||||
deprecatedSetting("indexer", "UPDATE_BUFFER_LEN", "queue.issue_indexer", "LENGTH")
|
||||
|
||||
Indexer.RepoIndexerEnabled = sec.Key("REPO_INDEXER_ENABLED").MustBool(false)
|
||||
Indexer.RepoType = sec.Key("REPO_INDEXER_TYPE").MustString("bleve")
|
||||
|
@ -25,22 +25,22 @@ var LFS = struct {
|
||||
Storage
|
||||
}{}
|
||||
|
||||
func loadLFSFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("server")
|
||||
func newLFSService() {
|
||||
sec := Cfg.Section("server")
|
||||
if err := sec.MapTo(&LFS); err != nil {
|
||||
log.Fatal("Failed to map LFS settings: %v", err)
|
||||
}
|
||||
|
||||
lfsSec := rootCfg.Section("lfs")
|
||||
lfsSec := Cfg.Section("lfs")
|
||||
storageType := lfsSec.Key("STORAGE_TYPE").MustString("")
|
||||
|
||||
// Specifically default PATH to LFS_CONTENT_PATH
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH")
|
||||
deprecatedSetting("server", "LFS_CONTENT_PATH", "lfs", "PATH")
|
||||
lfsSec.Key("PATH").MustString(
|
||||
sec.Key("LFS_CONTENT_PATH").String())
|
||||
|
||||
LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec)
|
||||
LFS.Storage = getStorage("lfs", storageType, lfsSec)
|
||||
|
||||
// Rest of LFS service settings
|
||||
if LFS.LocksPagingNum == 0 {
|
||||
|
@ -25,21 +25,6 @@ var (
|
||||
logDescriptions = make(map[string]*LogDescription)
|
||||
)
|
||||
|
||||
// Log settings
|
||||
var Log struct {
|
||||
Level log.Level
|
||||
StacktraceLogLevel string
|
||||
RootPath string
|
||||
EnableSSHLog bool
|
||||
EnableXORMLog bool
|
||||
|
||||
DisableRouterLog bool
|
||||
|
||||
EnableAccessLog bool
|
||||
AccessLogTemplate string
|
||||
BufferLength int64
|
||||
}
|
||||
|
||||
// GetLogDescriptions returns a race safe set of descriptions
|
||||
func GetLogDescriptions() map[string]*LogDescription {
|
||||
descriptionLock.RLock()
|
||||
@ -109,9 +94,9 @@ type defaultLogOptions struct {
|
||||
|
||||
func newDefaultLogOptions() defaultLogOptions {
|
||||
return defaultLogOptions{
|
||||
levelName: Log.Level.String(),
|
||||
levelName: LogLevel.String(),
|
||||
flags: "stdflags",
|
||||
filename: filepath.Join(Log.RootPath, "gitea.log"),
|
||||
filename: filepath.Join(LogRootPath, "gitea.log"),
|
||||
bufferLength: 10000,
|
||||
disableConsole: false,
|
||||
}
|
||||
@ -140,33 +125,10 @@ func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) strin
|
||||
return log.FromString(value).String()
|
||||
}
|
||||
|
||||
func loadLogFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("log")
|
||||
Log.Level = getLogLevel(sec, "LEVEL", log.INFO)
|
||||
Log.StacktraceLogLevel = getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", "None")
|
||||
Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
|
||||
forcePathSeparator(Log.RootPath)
|
||||
Log.BufferLength = sec.Key("BUFFER_LEN").MustInt64(10000)
|
||||
|
||||
Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false)
|
||||
Log.EnableAccessLog = sec.Key("ENABLE_ACCESS_LOG").MustBool(false)
|
||||
Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(
|
||||
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
|
||||
)
|
||||
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
|
||||
_ = rootCfg.Section("log").Key("ACCESS").MustString("file")
|
||||
|
||||
sec.Key("ROUTER").MustString("console")
|
||||
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
|
||||
Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool(Log.DisableRouterLog)
|
||||
|
||||
Log.EnableXORMLog = rootCfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
|
||||
}
|
||||
|
||||
func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
|
||||
level := getLogLevel(sec, "LEVEL", Log.Level)
|
||||
level := getLogLevel(sec, "LEVEL", LogLevel)
|
||||
levelName = level.String()
|
||||
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel)
|
||||
stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel)
|
||||
stacktraceLevel := log.FromString(stacktraceLevelName)
|
||||
mode = name
|
||||
keys := sec.Keys()
|
||||
@ -182,7 +144,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
|
||||
logPath = key.MustString(defaults.filename)
|
||||
forcePathSeparator(logPath)
|
||||
if !filepath.IsAbs(logPath) {
|
||||
logPath = path.Join(Log.RootPath, logPath)
|
||||
logPath = path.Join(LogRootPath, logPath)
|
||||
}
|
||||
case "FLAGS":
|
||||
flags = log.FlagsFromString(key.MustString(defaults.flags))
|
||||
@ -251,12 +213,12 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
|
||||
return mode, jsonConfig, levelName
|
||||
}
|
||||
|
||||
func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogOptions) *LogDescription {
|
||||
func generateNamedLogger(key string, options defaultLogOptions) *LogDescription {
|
||||
description := LogDescription{
|
||||
Name: key,
|
||||
}
|
||||
|
||||
sections := strings.Split(rootCfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",")
|
||||
sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",")
|
||||
|
||||
for i := 0; i < len(sections); i++ {
|
||||
sections[i] = strings.TrimSpace(sections[i])
|
||||
@ -266,9 +228,9 @@ func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogO
|
||||
if len(name) == 0 || (name == "console" && options.disableConsole) {
|
||||
continue
|
||||
}
|
||||
sec, err := rootCfg.GetSection("log." + name + "." + key)
|
||||
sec, err := Cfg.GetSection("log." + name + "." + key)
|
||||
if err != nil {
|
||||
sec, _ = rootCfg.NewSection("log." + name + "." + key)
|
||||
sec, _ = Cfg.NewSection("log." + name + "." + key)
|
||||
}
|
||||
|
||||
provider, config, levelName := generateLogConfig(sec, name, options)
|
||||
@ -291,17 +253,46 @@ func generateNamedLogger(rootCfg ConfigProvider, key string, options defaultLogO
|
||||
return &description
|
||||
}
|
||||
|
||||
// initLogFrom initializes logging with settings from configuration provider
|
||||
func initLogFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("log")
|
||||
func newAccessLogService() {
|
||||
EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false)
|
||||
AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString(
|
||||
`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`,
|
||||
)
|
||||
// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
|
||||
_ = Cfg.Section("log").Key("ACCESS").MustString("file")
|
||||
if EnableAccessLog {
|
||||
options := newDefaultLogOptions()
|
||||
options.filename = filepath.Join(LogRootPath, "access.log")
|
||||
options.flags = "" // For the router we don't want any prefixed flags
|
||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
||||
generateNamedLogger("access", options)
|
||||
}
|
||||
}
|
||||
|
||||
func newRouterLogService() {
|
||||
Cfg.Section("log").Key("ROUTER").MustString("console")
|
||||
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
|
||||
DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog)
|
||||
|
||||
if !DisableRouterLog {
|
||||
options := newDefaultLogOptions()
|
||||
options.filename = filepath.Join(LogRootPath, "router.log")
|
||||
options.flags = "date,time" // For the router we don't want any prefixed flags
|
||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
||||
generateNamedLogger("router", options)
|
||||
}
|
||||
}
|
||||
|
||||
func newLogService() {
|
||||
options := newDefaultLogOptions()
|
||||
options.bufferLength = Log.BufferLength
|
||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
||||
EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false)
|
||||
|
||||
description := LogDescription{
|
||||
Name: log.DEFAULT,
|
||||
}
|
||||
|
||||
sections := strings.Split(sec.Key("MODE").MustString("console"), ",")
|
||||
sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
|
||||
|
||||
useConsole := false
|
||||
for _, name := range sections {
|
||||
@ -313,11 +304,11 @@ func initLogFrom(rootCfg ConfigProvider) {
|
||||
useConsole = true
|
||||
}
|
||||
|
||||
sec, err := rootCfg.GetSection("log." + name + ".default")
|
||||
sec, err := Cfg.GetSection("log." + name + ".default")
|
||||
if err != nil {
|
||||
sec, err = rootCfg.GetSection("log." + name)
|
||||
sec, err = Cfg.GetSection("log." + name)
|
||||
if err != nil {
|
||||
sec, _ = rootCfg.NewSection("log." + name)
|
||||
sec, _ = Cfg.NewSection("log." + name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,45 +340,27 @@ func initLogFrom(rootCfg ConfigProvider) {
|
||||
// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
|
||||
func RestartLogsWithPIDSuffix() {
|
||||
filenameSuffix = fmt.Sprintf(".%d", os.Getpid())
|
||||
InitLogs(false)
|
||||
NewLogServices(false)
|
||||
}
|
||||
|
||||
// InitLogs creates all the log services
|
||||
func InitLogs(disableConsole bool) {
|
||||
initLogFrom(CfgProvider)
|
||||
|
||||
if !Log.DisableRouterLog {
|
||||
options := newDefaultLogOptions()
|
||||
options.filename = filepath.Join(Log.RootPath, "router.log")
|
||||
options.flags = "date,time" // For the router we don't want any prefixed flags
|
||||
options.bufferLength = Log.BufferLength
|
||||
generateNamedLogger(CfgProvider, "router", options)
|
||||
}
|
||||
|
||||
if Log.EnableAccessLog {
|
||||
options := newDefaultLogOptions()
|
||||
options.filename = filepath.Join(Log.RootPath, "access.log")
|
||||
options.flags = "" // For the router we don't want any prefixed flags
|
||||
options.bufferLength = Log.BufferLength
|
||||
generateNamedLogger(CfgProvider, "access", options)
|
||||
}
|
||||
|
||||
initSQLLogFrom(CfgProvider, disableConsole)
|
||||
// NewLogServices creates all the log services
|
||||
func NewLogServices(disableConsole bool) {
|
||||
newLogService()
|
||||
newRouterLogService()
|
||||
newAccessLogService()
|
||||
NewXORMLogService(disableConsole)
|
||||
}
|
||||
|
||||
// InitSQLLog initializes xorm logger setting
|
||||
func InitSQLLog(disableConsole bool) {
|
||||
initSQLLogFrom(CfgProvider, disableConsole)
|
||||
}
|
||||
|
||||
func initSQLLogFrom(rootCfg ConfigProvider, disableConsole bool) {
|
||||
if Log.EnableXORMLog {
|
||||
// NewXORMLogService initializes xorm logger service
|
||||
func NewXORMLogService(disableConsole bool) {
|
||||
EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
|
||||
if EnableXORMLog {
|
||||
options := newDefaultLogOptions()
|
||||
options.filename = filepath.Join(Log.RootPath, "xorm.log")
|
||||
options.bufferLength = Log.BufferLength
|
||||
options.filename = filepath.Join(LogRootPath, "xorm.log")
|
||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
||||
options.disableConsole = disableConsole
|
||||
|
||||
rootCfg.Section("log").Key("XORM").MustString(",")
|
||||
generateNamedLogger(rootCfg, "xorm", options)
|
||||
Cfg.Section("log").Key("XORM").MustString(",")
|
||||
generateNamedLogger("xorm", options)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
shellquote "github.com/kballard/go-shellquote"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// Mailer represents mail service.
|
||||
@ -49,14 +50,7 @@ type Mailer struct {
|
||||
// MailService the global mailer
|
||||
var MailService *Mailer
|
||||
|
||||
func loadMailsFrom(rootCfg ConfigProvider) {
|
||||
loadMailerFrom(rootCfg)
|
||||
loadRegisterMailFrom(rootCfg)
|
||||
loadNotifyMailFrom(rootCfg)
|
||||
loadIncomingEmailFrom(rootCfg)
|
||||
}
|
||||
|
||||
func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
func parseMailerConfig(rootCfg *ini.File) {
|
||||
sec := rootCfg.Section("mailer")
|
||||
// Check mailer setting.
|
||||
if !sec.Key("ENABLED").MustBool() {
|
||||
@ -65,7 +59,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
|
||||
// Handle Deprecations and map on to new configuration
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
|
||||
deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
|
||||
if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
|
||||
if sec.Key("MAILER_TYPE").String() == "sendmail" {
|
||||
sec.Key("PROTOCOL").MustString("sendmail")
|
||||
@ -73,7 +67,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "HOST", "mailer", "SMTP_ADDR")
|
||||
deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR")
|
||||
if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
|
||||
givenHost := sec.Key("HOST").String()
|
||||
addr, port, err := net.SplitHostPort(givenHost)
|
||||
@ -90,7 +84,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
|
||||
deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
|
||||
if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
|
||||
if sec.Key("IS_TLS_ENABLED").MustBool() {
|
||||
sec.Key("PROTOCOL").MustString("smtps")
|
||||
@ -100,37 +94,37 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO")
|
||||
deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO")
|
||||
if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") {
|
||||
sec.Key("ENABLE_HELO").MustBool(!sec.Key("DISABLE_HELO").MustBool())
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT")
|
||||
deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT")
|
||||
if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") {
|
||||
sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(sec.Key("SKIP_VERIFY").MustBool())
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT")
|
||||
deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT")
|
||||
if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") {
|
||||
sec.Key("USE_CLIENT_CERT").MustBool(sec.Key("USE_CERTIFICATE").MustBool())
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE")
|
||||
deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE")
|
||||
if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") {
|
||||
sec.Key("CERT_FILE").MustString(sec.Key("CERT_FILE").String())
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE")
|
||||
deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE")
|
||||
if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") {
|
||||
sec.Key("KEY_FILE").MustString(sec.Key("KEY_FILE").String())
|
||||
}
|
||||
|
||||
// FIXME: DEPRECATED to be removed in v1.19.0
|
||||
deprecatedSetting(rootCfg, "mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
|
||||
deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
|
||||
if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
|
||||
sec.Key("SEND_AS_PLAIN_TEXT").MustBool(!sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false))
|
||||
}
|
||||
@ -243,8 +237,8 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||
log.Info("Mail Service Enabled")
|
||||
}
|
||||
|
||||
func loadRegisterMailFrom(rootCfg ConfigProvider) {
|
||||
if !rootCfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
|
||||
func newRegisterMailService() {
|
||||
if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
|
||||
return
|
||||
} else if MailService == nil {
|
||||
log.Warn("Register Mail Service: Mail Service is not enabled")
|
||||
@ -254,8 +248,8 @@ func loadRegisterMailFrom(rootCfg ConfigProvider) {
|
||||
log.Info("Register Mail Service Enabled")
|
||||
}
|
||||
|
||||
func loadNotifyMailFrom(rootCfg ConfigProvider) {
|
||||
if !rootCfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
|
||||
func newNotifyMailService() {
|
||||
if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
|
||||
return
|
||||
} else if MailService == nil {
|
||||
log.Warn("Notify Mail Service: Mail Service is not enabled")
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func Test_loadMailerFrom(t *testing.T) {
|
||||
func TestParseMailerConfig(t *testing.T) {
|
||||
iniFile := ini.Empty()
|
||||
kases := map[string]*Mailer{
|
||||
"smtp.mydomain.com": {
|
||||
@ -34,7 +34,7 @@ func Test_loadMailerFrom(t *testing.T) {
|
||||
sec.NewKey("HOST", host)
|
||||
|
||||
// Check mailer setting
|
||||
loadMailerFrom(iniFile)
|
||||
parseMailerConfig(iniFile)
|
||||
|
||||
assert.EqualValues(t, kase.SMTPAddr, MailService.SMTPAddr)
|
||||
assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort)
|
||||
|
@ -25,20 +25,6 @@ const (
|
||||
RenderContentModeIframe = "iframe"
|
||||
)
|
||||
|
||||
// Markdown settings
|
||||
var Markdown = struct {
|
||||
EnableHardLineBreakInComments bool
|
||||
EnableHardLineBreakInDocuments bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
EnableMath bool
|
||||
}{
|
||||
EnableHardLineBreakInComments: true,
|
||||
EnableHardLineBreakInDocuments: false,
|
||||
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
|
||||
EnableMath: true,
|
||||
}
|
||||
|
||||
// MarkupRenderer defines the external parser configured in ini
|
||||
type MarkupRenderer struct {
|
||||
Enabled bool
|
||||
@ -60,14 +46,12 @@ type MarkupSanitizerRule struct {
|
||||
AllowDataURIImages bool
|
||||
}
|
||||
|
||||
func loadMarkupFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "markdown", &Markdown)
|
||||
|
||||
MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000)
|
||||
func newMarkup() {
|
||||
MermaidMaxSourceCharacters = Cfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000)
|
||||
ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10)
|
||||
ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10)
|
||||
|
||||
for _, sec := range rootCfg.Section("markup").ChildSections() {
|
||||
for _, sec := range Cfg.Section("markup").ChildSections() {
|
||||
name := strings.TrimPrefix(sec.Name(), "markup.")
|
||||
if name == "" {
|
||||
log.Warn("name is empty, markup " + sec.Name() + "ignored")
|
||||
|
@ -1,21 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
// Metrics settings
|
||||
var Metrics = struct {
|
||||
Enabled bool
|
||||
Token string
|
||||
EnabledIssueByLabel bool
|
||||
EnabledIssueByRepository bool
|
||||
}{
|
||||
Enabled: false,
|
||||
Token: "",
|
||||
EnabledIssueByLabel: false,
|
||||
EnabledIssueByRepository: false,
|
||||
}
|
||||
|
||||
func loadMetricsFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "metrics", &Metrics)
|
||||
}
|
@ -16,8 +16,8 @@ var Migrations = struct {
|
||||
RetryBackoff: 3,
|
||||
}
|
||||
|
||||
func loadMigrationsFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("migrations")
|
||||
func newMigrationsService() {
|
||||
sec := Cfg.Section("migrations")
|
||||
Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts)
|
||||
Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff)
|
||||
|
||||
|
@ -14,8 +14,8 @@ var MimeTypeMap = struct {
|
||||
Map: map[string]string{},
|
||||
}
|
||||
|
||||
func loadMimeTypeMapFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("repository.mimetype_mapping")
|
||||
func newMimeTypeMap() {
|
||||
sec := Cfg.Section("repository.mimetype_mapping")
|
||||
keys := sec.Keys()
|
||||
m := make(map[string]string, len(keys))
|
||||
for _, key := range keys {
|
||||
|
@ -24,16 +24,16 @@ var Mirror = struct {
|
||||
DefaultInterval: 8 * time.Hour,
|
||||
}
|
||||
|
||||
func loadMirrorFrom(rootCfg ConfigProvider) {
|
||||
func newMirror() {
|
||||
// Handle old configuration through `[repository]` `DISABLE_MIRRORS`
|
||||
// - please note this was badly named and only disabled the creation of new pull mirrors
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
deprecatedSetting(rootCfg, "repository", "DISABLE_MIRRORS", "mirror", "ENABLED")
|
||||
if rootCfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) {
|
||||
deprecatedSetting("repository", "DISABLE_MIRRORS", "mirror", "ENABLED")
|
||||
if Cfg.Section("repository").Key("DISABLE_MIRRORS").MustBool(false) {
|
||||
Mirror.DisableNewPull = true
|
||||
}
|
||||
|
||||
if err := rootCfg.Section("mirror").MapTo(&Mirror); err != nil {
|
||||
if err := Cfg.Section("mirror").MapTo(&Mirror); err != nil {
|
||||
log.Fatal("Failed to map Mirror settings: %v", err)
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,6 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"math"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
@ -62,8 +59,8 @@ var OAuth2Client struct {
|
||||
AccountLinking OAuth2AccountLinkingType
|
||||
}
|
||||
|
||||
func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("oauth2_client")
|
||||
func newOAuth2Client() {
|
||||
sec := Cfg.Section("oauth2_client")
|
||||
OAuth2Client.RegisterEmailConfirm = sec.Key("REGISTER_EMAIL_CONFIRM").MustBool(Service.RegisterEmailConfirm)
|
||||
OAuth2Client.OpenIDConnectScopes = parseScopes(sec, "OPENID_CONNECT_SCOPES")
|
||||
OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
|
||||
@ -90,33 +87,3 @@ func parseScopes(sec *ini.Section, name string) []string {
|
||||
}
|
||||
return scopes
|
||||
}
|
||||
|
||||
var OAuth2 = struct {
|
||||
Enable bool
|
||||
AccessTokenExpirationTime int64
|
||||
RefreshTokenExpirationTime int64
|
||||
InvalidateRefreshTokens bool
|
||||
JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"`
|
||||
JWTSecretBase64 string `ini:"JWT_SECRET"`
|
||||
JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"`
|
||||
MaxTokenLength int
|
||||
}{
|
||||
Enable: true,
|
||||
AccessTokenExpirationTime: 3600,
|
||||
RefreshTokenExpirationTime: 730,
|
||||
InvalidateRefreshTokens: false,
|
||||
JWTSigningAlgorithm: "RS256",
|
||||
JWTSigningPrivateKeyFile: "jwt/private.pem",
|
||||
MaxTokenLength: math.MaxInt16,
|
||||
}
|
||||
|
||||
func loadOAuth2From(rootCfg ConfigProvider) {
|
||||
if err := rootCfg.Section("oauth2").MapTo(&OAuth2); err != nil {
|
||||
log.Fatal("Failed to OAuth2 settings: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
|
||||
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
var (
|
||||
// Other settings
|
||||
ShowFooterBranding bool
|
||||
ShowFooterVersion bool
|
||||
ShowFooterTemplateLoadTime bool
|
||||
EnableFeed bool
|
||||
EnableSitemap bool
|
||||
)
|
||||
|
||||
func loadOtherFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("other")
|
||||
ShowFooterBranding = sec.Key("SHOW_FOOTER_BRANDING").MustBool(false)
|
||||
ShowFooterVersion = sec.Key("SHOW_FOOTER_VERSION").MustBool(true)
|
||||
ShowFooterTemplateLoadTime = sec.Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
|
||||
EnableSitemap = sec.Key("ENABLE_SITEMAP").MustBool(true)
|
||||
EnableFeed = sec.Key("ENABLE_FEED").MustBool(true)
|
||||
}
|
@ -46,13 +46,13 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func loadPackagesFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("packages")
|
||||
func newPackages() {
|
||||
sec := Cfg.Section("packages")
|
||||
if err := sec.MapTo(&Packages); err != nil {
|
||||
log.Fatal("Failed to map Packages settings: %v", err)
|
||||
}
|
||||
|
||||
Packages.Storage = getStorage(rootCfg, "packages", "", nil)
|
||||
Packages.Storage = getStorage("packages", "", nil)
|
||||
|
||||
appURL, _ := url.Parse(AppURL)
|
||||
Packages.RegistryHost = appURL.Host
|
||||
|
@ -32,16 +32,16 @@ var (
|
||||
}{}
|
||||
)
|
||||
|
||||
func loadPictureFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("picture")
|
||||
func newPictureService() {
|
||||
sec := Cfg.Section("picture")
|
||||
|
||||
avatarSec := rootCfg.Section("avatar")
|
||||
avatarSec := Cfg.Section("avatar")
|
||||
storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("")
|
||||
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
||||
avatarSec.Key("PATH").MustString(
|
||||
sec.Key("AVATAR_UPLOAD_PATH").String())
|
||||
|
||||
Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec)
|
||||
Avatar.Storage = getStorage("avatars", storageType, avatarSec)
|
||||
|
||||
Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096)
|
||||
Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(3072)
|
||||
@ -60,11 +60,11 @@ func loadPictureFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
|
||||
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
|
||||
deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR")
|
||||
deprecatedSettingDB("", "DISABLE_GRAVATAR")
|
||||
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
|
||||
deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
|
||||
deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR")
|
||||
|
||||
loadRepoAvatarFrom(rootCfg)
|
||||
newRepoAvatarService()
|
||||
}
|
||||
|
||||
func GetDefaultDisableGravatar() bool {
|
||||
@ -82,16 +82,16 @@ func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
|
||||
return v
|
||||
}
|
||||
|
||||
func loadRepoAvatarFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("picture")
|
||||
func newRepoAvatarService() {
|
||||
sec := Cfg.Section("picture")
|
||||
|
||||
repoAvatarSec := rootCfg.Section("repo-avatar")
|
||||
repoAvatarSec := Cfg.Section("repo-avatar")
|
||||
storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("")
|
||||
// Specifically default PATH to AVATAR_UPLOAD_PATH
|
||||
repoAvatarSec.Key("PATH").MustString(
|
||||
sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String())
|
||||
|
||||
RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec)
|
||||
RepoAvatar.Storage = getStorage("repo-avatars", storageType, repoAvatarSec)
|
||||
|
||||
RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none")
|
||||
RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString("/assets/img/repo_default.png")
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
package setting
|
||||
|
||||
import "code.gitea.io/gitea/modules/log"
|
||||
|
||||
// Project settings
|
||||
var (
|
||||
Project = struct {
|
||||
@ -14,6 +16,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func loadProjectFrom(rootCfg ConfigProvider) {
|
||||
mustMapSetting(rootCfg, "project", &Project)
|
||||
func newProject() {
|
||||
if err := Cfg.Section("project").MapTo(&Project); err != nil {
|
||||
log.Fatal("Failed to map Project settings: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ var Proxy = struct {
|
||||
ProxyHosts: []string{},
|
||||
}
|
||||
|
||||
func loadProxyFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("proxy")
|
||||
func newProxyService() {
|
||||
sec := Cfg.Section("proxy")
|
||||
Proxy.Enabled = sec.Key("PROXY_ENABLED").MustBool(false)
|
||||
Proxy.ProxyURL = sec.Key("PROXY_URL").MustString("")
|
||||
if Proxy.ProxyURL != "" {
|
||||
|
@ -39,12 +39,8 @@ var Queue = QueueSettings{}
|
||||
|
||||
// GetQueueSettings returns the queue settings for the appropriately named queue
|
||||
func GetQueueSettings(name string) QueueSettings {
|
||||
return getQueueSettings(CfgProvider, name)
|
||||
}
|
||||
|
||||
func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings {
|
||||
q := QueueSettings{}
|
||||
sec := rootCfg.Section("queue." + name)
|
||||
sec := Cfg.Section("queue." + name)
|
||||
q.Name = name
|
||||
|
||||
// DataDir is not directly inheritable
|
||||
@ -86,14 +82,10 @@ func getQueueSettings(rootCfg ConfigProvider, name string) QueueSettings {
|
||||
return q
|
||||
}
|
||||
|
||||
// LoadQueueSettings sets up the default settings for Queues
|
||||
// NewQueueService sets up the default settings for Queues
|
||||
// This is exported for tests to be able to use the queue
|
||||
func LoadQueueSettings() {
|
||||
loadQueueFrom(CfgProvider)
|
||||
}
|
||||
|
||||
func loadQueueFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("queue")
|
||||
func NewQueueService() {
|
||||
sec := Cfg.Section("queue")
|
||||
Queue.DataDir = filepath.ToSlash(sec.Key("DATADIR").MustString("queues/"))
|
||||
if !filepath.IsAbs(Queue.DataDir) {
|
||||
Queue.DataDir = filepath.ToSlash(filepath.Join(AppDataPath, Queue.DataDir))
|
||||
@ -116,10 +108,10 @@ func loadQueueFrom(rootCfg ConfigProvider) {
|
||||
|
||||
// Now handle the old issue_indexer configuration
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
section := rootCfg.Section("queue.issue_indexer")
|
||||
section := Cfg.Section("queue.issue_indexer")
|
||||
directlySet := toDirectlySetKeysSet(section)
|
||||
if !directlySet.Contains("TYPE") && defaultType == "" {
|
||||
switch typ := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
|
||||
switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
|
||||
case "levelqueue":
|
||||
_, _ = section.NewKey("TYPE", "level")
|
||||
case "channel":
|
||||
@ -133,25 +125,25 @@ func loadQueueFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
}
|
||||
if !directlySet.Contains("LENGTH") {
|
||||
length := rootCfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
|
||||
length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
|
||||
if length != 0 {
|
||||
_, _ = section.NewKey("LENGTH", strconv.Itoa(length))
|
||||
}
|
||||
}
|
||||
if !directlySet.Contains("BATCH_LENGTH") {
|
||||
fallback := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
|
||||
fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
|
||||
if fallback != 0 {
|
||||
_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback))
|
||||
}
|
||||
}
|
||||
if !directlySet.Contains("DATADIR") {
|
||||
queueDir := filepath.ToSlash(rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
|
||||
queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
|
||||
if queueDir != "" {
|
||||
_, _ = section.NewKey("DATADIR", queueDir)
|
||||
}
|
||||
}
|
||||
if !directlySet.Contains("CONN_STR") {
|
||||
connStr := rootCfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
|
||||
connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
|
||||
if connStr != "" {
|
||||
_, _ = section.NewKey("CONN_STR", connStr)
|
||||
}
|
||||
@ -161,31 +153,31 @@ func loadQueueFrom(rootCfg ConfigProvider) {
|
||||
// - will need to set default for [queue.*)] LENGTH appropriately though though
|
||||
|
||||
// Handle the old mailer configuration
|
||||
handleOldLengthConfiguration(rootCfg, "mailer", "mailer", "SEND_BUFFER_LEN", 100)
|
||||
handleOldLengthConfiguration("mailer", "mailer", "SEND_BUFFER_LEN", 100)
|
||||
|
||||
// Handle the old test pull requests configuration
|
||||
// Please note this will be a unique queue
|
||||
handleOldLengthConfiguration(rootCfg, "pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000)
|
||||
handleOldLengthConfiguration("pr_patch_checker", "repository", "PULL_REQUEST_QUEUE_LENGTH", 1000)
|
||||
|
||||
// Handle the old mirror queue configuration
|
||||
// Please note this will be a unique queue
|
||||
handleOldLengthConfiguration(rootCfg, "mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000)
|
||||
handleOldLengthConfiguration("mirror", "repository", "MIRROR_QUEUE_LENGTH", 1000)
|
||||
}
|
||||
|
||||
// handleOldLengthConfiguration allows fallback to older configuration. `[queue.name]` `LENGTH` will override this configuration, but
|
||||
// if that is left unset then we should fallback to the older configuration. (Except where the new length woul be <=0)
|
||||
func handleOldLengthConfiguration(rootCfg ConfigProvider, queueName, oldSection, oldKey string, defaultValue int) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultValue int) {
|
||||
if Cfg.Section(oldSection).HasKey(oldKey) {
|
||||
log.Error("Deprecated fallback for %s queue length `[%s]` `%s` present. Use `[queue.%s]` `LENGTH`. This will be removed in v1.18.0", queueName, queueName, oldSection, oldKey)
|
||||
}
|
||||
value := rootCfg.Section(oldSection).Key(oldKey).MustInt(defaultValue)
|
||||
value := Cfg.Section(oldSection).Key(oldKey).MustInt(defaultValue)
|
||||
|
||||
// Don't override with 0
|
||||
if value <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
section := rootCfg.Section("queue." + queueName)
|
||||
section := Cfg.Section("queue." + queueName)
|
||||
directlySet := toDirectlySetKeysSet(section)
|
||||
if !directlySet.Contains("LENGTH") {
|
||||
_, _ = section.NewKey("LENGTH", strconv.Itoa(value))
|
||||
|
@ -270,10 +270,10 @@ var (
|
||||
}{}
|
||||
)
|
||||
|
||||
func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||
func newRepository() {
|
||||
var err error
|
||||
// Determine and create root git repository path.
|
||||
sec := rootCfg.Section("repository")
|
||||
sec := Cfg.Section("repository")
|
||||
Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool()
|
||||
Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool()
|
||||
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
|
||||
@ -295,19 +295,19 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||
log.Warn("SCRIPT_TYPE %q is not on the current PATH. Are you sure that this is the correct SCRIPT_TYPE?", ScriptType)
|
||||
}
|
||||
|
||||
if err = sec.MapTo(&Repository); err != nil {
|
||||
if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
|
||||
log.Fatal("Failed to map Repository settings: %v", err)
|
||||
} else if err = rootCfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
|
||||
} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
|
||||
log.Fatal("Failed to map Repository.Editor settings: %v", err)
|
||||
} else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
|
||||
} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
|
||||
log.Fatal("Failed to map Repository.Upload settings: %v", err)
|
||||
} else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
|
||||
} else if err = Cfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
|
||||
log.Fatal("Failed to map Repository.Local settings: %v", err)
|
||||
} else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil {
|
||||
} else if err = Cfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil {
|
||||
log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
|
||||
}
|
||||
|
||||
if !rootCfg.Section("packages").Key("ENABLED").MustBool(true) {
|
||||
if !Cfg.Section("packages").Key("ENABLED").MustBool(true) {
|
||||
Repository.DisabledRepoUnits = append(Repository.DisabledRepoUnits, "repo.packages")
|
||||
}
|
||||
|
||||
@ -354,5 +354,5 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
|
||||
Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath)
|
||||
}
|
||||
|
||||
RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil)
|
||||
RepoArchive.Storage = getStorage("repo-archive", "", nil)
|
||||
}
|
||||
|
@ -1,158 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Security settings
|
||||
InstallLock bool
|
||||
SecretKey string
|
||||
InternalToken string // internal access token
|
||||
LogInRememberDays int
|
||||
CookieUserName string
|
||||
CookieRememberName string
|
||||
ReverseProxyAuthUser string
|
||||
ReverseProxyAuthEmail string
|
||||
ReverseProxyAuthFullName string
|
||||
ReverseProxyLimit int
|
||||
ReverseProxyTrustedProxies []string
|
||||
MinPasswordLength int
|
||||
ImportLocalPaths bool
|
||||
DisableGitHooks bool
|
||||
DisableWebhooks bool
|
||||
OnlyAllowPushIfGiteaEnvironmentSet bool
|
||||
PasswordComplexity []string
|
||||
PasswordHashAlgo string
|
||||
PasswordCheckPwn bool
|
||||
SuccessfulTokensCacheSize int
|
||||
CSRFCookieName = "_csrf"
|
||||
CSRFCookieHTTPOnly = true
|
||||
)
|
||||
|
||||
// loadSecret load the secret from ini by uriKey or verbatimKey, only one of them could be set
|
||||
// If the secret is loaded from uriKey (file), the file should be non-empty, to guarantee the behavior stable and clear.
|
||||
func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
|
||||
// don't allow setting both URI and verbatim string
|
||||
uri := sec.Key(uriKey).String()
|
||||
verbatim := sec.Key(verbatimKey).String()
|
||||
if uri != "" && verbatim != "" {
|
||||
log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey)
|
||||
}
|
||||
|
||||
// if we have no URI, use verbatim
|
||||
if uri == "" {
|
||||
return verbatim
|
||||
}
|
||||
|
||||
tempURI, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err)
|
||||
}
|
||||
switch tempURI.Scheme {
|
||||
case "file":
|
||||
buf, err := os.ReadFile(tempURI.RequestURI())
|
||||
if err != nil {
|
||||
log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err)
|
||||
}
|
||||
val := strings.TrimSpace(string(buf))
|
||||
if val == "" {
|
||||
// The file shouldn't be empty, otherwise we can not know whether the user has ever set the KEY or KEY_URI
|
||||
// For example: if INTERNAL_TOKEN_URI=file:///empty-file,
|
||||
// Then if the token is re-generated during installation and saved to INTERNAL_TOKEN
|
||||
// Then INTERNAL_TOKEN and INTERNAL_TOKEN_URI both exist, that's a fatal error (they shouldn't)
|
||||
log.Fatal("Failed to read %s (%s): the file is empty", uriKey, tempURI.RequestURI())
|
||||
}
|
||||
return val
|
||||
|
||||
// only file URIs are allowed
|
||||
default:
|
||||
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// generateSaveInternalToken generates and saves the internal token to app.ini
|
||||
func generateSaveInternalToken() {
|
||||
token, err := generate.NewInternalToken()
|
||||
if err != nil {
|
||||
log.Fatal("Error generate internal token: %v", err)
|
||||
}
|
||||
|
||||
InternalToken = token
|
||||
CreateOrAppendToCustomConf("security.INTERNAL_TOKEN", func(cfg *ini.File) {
|
||||
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
|
||||
})
|
||||
}
|
||||
|
||||
func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("security")
|
||||
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
||||
SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
|
||||
if SecretKey == "" {
|
||||
// FIXME: https://github.com/go-gitea/gitea/issues/16832
|
||||
// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
|
||||
SecretKey = "!#@FDEWREWR&*(" //nolint:gosec
|
||||
}
|
||||
|
||||
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
|
||||
|
||||
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
|
||||
ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
|
||||
ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME")
|
||||
|
||||
ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
|
||||
ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
|
||||
if len(ReverseProxyTrustedProxies) == 0 {
|
||||
ReverseProxyTrustedProxies = []string{"127.0.0.0/8", "::1/128"}
|
||||
}
|
||||
|
||||
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
|
||||
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
|
||||
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
|
||||
DisableWebhooks = sec.Key("DISABLE_WEBHOOKS").MustBool(false)
|
||||
OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
|
||||
|
||||
// Ensure that the provided default hash algorithm is a valid hash algorithm
|
||||
var algorithm *hash.PasswordHashAlgorithm
|
||||
PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(sec.Key("PASSWORD_HASH_ALGO").MustString(""))
|
||||
if algorithm == nil {
|
||||
log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString(""))
|
||||
}
|
||||
|
||||
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
|
||||
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
|
||||
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
|
||||
|
||||
InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
|
||||
if InstallLock && InternalToken == "" {
|
||||
// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
|
||||
// some users do cluster deployment, they still depend on this auto-generating behavior.
|
||||
generateSaveInternalToken()
|
||||
}
|
||||
|
||||
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
|
||||
if len(cfgdata) == 0 {
|
||||
cfgdata = []string{"off"}
|
||||
}
|
||||
PasswordComplexity = make([]string, 0, len(cfgdata))
|
||||
for _, name := range cfgdata {
|
||||
name := strings.ToLower(strings.Trim(name, `"`))
|
||||
if name != "" {
|
||||
PasswordComplexity = append(PasswordComplexity, name)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,356 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Scheme describes protocol types
|
||||
type Scheme string
|
||||
|
||||
// enumerates all the scheme types
|
||||
const (
|
||||
HTTP Scheme = "http"
|
||||
HTTPS Scheme = "https"
|
||||
FCGI Scheme = "fcgi"
|
||||
FCGIUnix Scheme = "fcgi+unix"
|
||||
HTTPUnix Scheme = "http+unix"
|
||||
)
|
||||
|
||||
// LandingPage describes the default page
|
||||
type LandingPage string
|
||||
|
||||
// enumerates all the landing page types
|
||||
const (
|
||||
LandingPageHome LandingPage = "/"
|
||||
LandingPageExplore LandingPage = "/explore"
|
||||
LandingPageOrganizations LandingPage = "/explore/organizations"
|
||||
LandingPageLogin LandingPage = "/user/login"
|
||||
)
|
||||
|
||||
var (
|
||||
// AppName is the Application name, used in the page title.
|
||||
// It maps to ini:"APP_NAME"
|
||||
AppName string
|
||||
// AppURL is the Application ROOT_URL. It always has a '/' suffix
|
||||
// It maps to ini:"ROOT_URL"
|
||||
AppURL string
|
||||
// AppSubURL represents the sub-url mounting point for gitea. It is either "" or starts with '/' and ends without '/', such as '/{subpath}'.
|
||||
// This value is empty if site does not have sub-url.
|
||||
AppSubURL string
|
||||
// AppDataPath is the default path for storing data.
|
||||
// It maps to ini:"APP_DATA_PATH" in [server] and defaults to AppWorkPath + "/data"
|
||||
AppDataPath string
|
||||
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
|
||||
// It maps to ini:"LOCAL_ROOT_URL" in [server]
|
||||
LocalURL string
|
||||
// AssetVersion holds a opaque value that is used for cache-busting assets
|
||||
AssetVersion string
|
||||
|
||||
// Server settings
|
||||
Protocol Scheme
|
||||
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
|
||||
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
|
||||
ProxyProtocolHeaderTimeout time.Duration
|
||||
ProxyProtocolAcceptUnknown bool
|
||||
Domain string
|
||||
HTTPAddr string
|
||||
HTTPPort string
|
||||
LocalUseProxyProtocol bool
|
||||
RedirectOtherPort bool
|
||||
RedirectorUseProxyProtocol bool
|
||||
PortToRedirect string
|
||||
OfflineMode bool
|
||||
CertFile string
|
||||
KeyFile string
|
||||
StaticRootPath string
|
||||
StaticCacheTime time.Duration
|
||||
EnableGzip bool
|
||||
LandingPageURL LandingPage
|
||||
LandingPageCustom string
|
||||
UnixSocketPermission uint32
|
||||
EnablePprof bool
|
||||
PprofDataPath string
|
||||
EnableAcme bool
|
||||
AcmeTOS bool
|
||||
AcmeLiveDirectory string
|
||||
AcmeEmail string
|
||||
AcmeURL string
|
||||
AcmeCARoot string
|
||||
SSLMinimumVersion string
|
||||
SSLMaximumVersion string
|
||||
SSLCurvePreferences []string
|
||||
SSLCipherSuites []string
|
||||
GracefulRestartable bool
|
||||
GracefulHammerTime time.Duration
|
||||
StartupTimeout time.Duration
|
||||
PerWriteTimeout = 30 * time.Second
|
||||
PerWritePerKbTimeout = 10 * time.Second
|
||||
StaticURLPrefix string
|
||||
AbsoluteAssetURL string
|
||||
|
||||
HasRobotsTxt bool
|
||||
ManifestData string
|
||||
)
|
||||
|
||||
// MakeManifestData generates web app manifest JSON
|
||||
func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
|
||||
type manifestIcon struct {
|
||||
Src string `json:"src"`
|
||||
Type string `json:"type"`
|
||||
Sizes string `json:"sizes"`
|
||||
}
|
||||
|
||||
type manifestJSON struct {
|
||||
Name string `json:"name"`
|
||||
ShortName string `json:"short_name"`
|
||||
StartURL string `json:"start_url"`
|
||||
Icons []manifestIcon `json:"icons"`
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(&manifestJSON{
|
||||
Name: appName,
|
||||
ShortName: appName,
|
||||
StartURL: appURL,
|
||||
Icons: []manifestIcon{
|
||||
{
|
||||
Src: absoluteAssetURL + "/assets/img/logo.png",
|
||||
Type: "image/png",
|
||||
Sizes: "512x512",
|
||||
},
|
||||
{
|
||||
Src: absoluteAssetURL + "/assets/img/logo.svg",
|
||||
Type: "image/svg+xml",
|
||||
Sizes: "512x512",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("unable to marshal manifest JSON. Error: %v", err)
|
||||
return make([]byte, 0)
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
|
||||
func MakeAbsoluteAssetURL(appURL, staticURLPrefix string) string {
|
||||
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
|
||||
if err != nil {
|
||||
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
|
||||
}
|
||||
|
||||
if err == nil && parsedPrefix.Hostname() == "" {
|
||||
if staticURLPrefix == "" {
|
||||
return strings.TrimSuffix(appURL, "/")
|
||||
}
|
||||
|
||||
// StaticURLPrefix is just a path
|
||||
return util.URLJoin(appURL, strings.TrimSuffix(staticURLPrefix, "/"))
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(staticURLPrefix, "/")
|
||||
}
|
||||
|
||||
func loadServerFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("server")
|
||||
AppName = rootCfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
|
||||
|
||||
Domain = sec.Key("DOMAIN").MustString("localhost")
|
||||
HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
|
||||
HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
|
||||
|
||||
Protocol = HTTP
|
||||
protocolCfg := sec.Key("PROTOCOL").String()
|
||||
switch protocolCfg {
|
||||
case "https":
|
||||
Protocol = HTTPS
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
if sec.HasKey("ENABLE_ACME") {
|
||||
EnableAcme = sec.Key("ENABLE_ACME").MustBool(false)
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "ENABLE_LETSENCRYPT", "server", "ENABLE_ACME")
|
||||
EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
|
||||
}
|
||||
if EnableAcme {
|
||||
AcmeURL = sec.Key("ACME_URL").MustString("")
|
||||
AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("")
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
if sec.HasKey("ACME_ACCEPTTOS") {
|
||||
AcmeTOS = sec.Key("ACME_ACCEPTTOS").MustBool(false)
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_ACCEPTTOS", "server", "ACME_ACCEPTTOS")
|
||||
AcmeTOS = sec.Key("LETSENCRYPT_ACCEPTTOS").MustBool(false)
|
||||
}
|
||||
if !AcmeTOS {
|
||||
log.Fatal("ACME TOS is not accepted (ACME_ACCEPTTOS).")
|
||||
}
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
if sec.HasKey("ACME_DIRECTORY") {
|
||||
AcmeLiveDirectory = sec.Key("ACME_DIRECTORY").MustString("https")
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_DIRECTORY", "server", "ACME_DIRECTORY")
|
||||
AcmeLiveDirectory = sec.Key("LETSENCRYPT_DIRECTORY").MustString("https")
|
||||
}
|
||||
// FIXME: DEPRECATED to be removed in v1.18.0
|
||||
if sec.HasKey("ACME_EMAIL") {
|
||||
AcmeEmail = sec.Key("ACME_EMAIL").MustString("")
|
||||
} else {
|
||||
deprecatedSetting(rootCfg, "server", "LETSENCRYPT_EMAIL", "server", "ACME_EMAIL")
|
||||
AcmeEmail = sec.Key("LETSENCRYPT_EMAIL").MustString("")
|
||||
}
|
||||
} else {
|
||||
CertFile = sec.Key("CERT_FILE").String()
|
||||
KeyFile = sec.Key("KEY_FILE").String()
|
||||
if len(CertFile) > 0 && !filepath.IsAbs(CertFile) {
|
||||
CertFile = filepath.Join(CustomPath, CertFile)
|
||||
}
|
||||
if len(KeyFile) > 0 && !filepath.IsAbs(KeyFile) {
|
||||
KeyFile = filepath.Join(CustomPath, KeyFile)
|
||||
}
|
||||
}
|
||||
SSLMinimumVersion = sec.Key("SSL_MIN_VERSION").MustString("")
|
||||
SSLMaximumVersion = sec.Key("SSL_MAX_VERSION").MustString("")
|
||||
SSLCurvePreferences = sec.Key("SSL_CURVE_PREFERENCES").Strings(",")
|
||||
SSLCipherSuites = sec.Key("SSL_CIPHER_SUITES").Strings(",")
|
||||
case "fcgi":
|
||||
Protocol = FCGI
|
||||
case "fcgi+unix", "unix", "http+unix":
|
||||
switch protocolCfg {
|
||||
case "fcgi+unix":
|
||||
Protocol = FCGIUnix
|
||||
case "unix":
|
||||
log.Warn("unix PROTOCOL value is deprecated, please use http+unix")
|
||||
fallthrough
|
||||
case "http+unix":
|
||||
Protocol = HTTPUnix
|
||||
}
|
||||
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
||||
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
|
||||
if err != nil || UnixSocketPermissionParsed > 0o777 {
|
||||
log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
|
||||
}
|
||||
|
||||
UnixSocketPermission = uint32(UnixSocketPermissionParsed)
|
||||
if !filepath.IsAbs(HTTPAddr) {
|
||||
HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr)
|
||||
}
|
||||
}
|
||||
UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false)
|
||||
ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false)
|
||||
ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second)
|
||||
ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false)
|
||||
GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
|
||||
GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
|
||||
StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
|
||||
PerWriteTimeout = sec.Key("PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
|
||||
PerWritePerKbTimeout = sec.Key("PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
|
||||
|
||||
defaultAppURL := string(Protocol) + "://" + Domain + ":" + HTTPPort
|
||||
AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
|
||||
|
||||
// Check validity of AppURL
|
||||
appURL, err := url.Parse(AppURL)
|
||||
if err != nil {
|
||||
log.Fatal("Invalid ROOT_URL '%s': %s", AppURL, err)
|
||||
}
|
||||
// Remove default ports from AppURL.
|
||||
// (scheme-based URL normalization, RFC 3986 section 6.2.3)
|
||||
if (appURL.Scheme == string(HTTP) && appURL.Port() == "80") || (appURL.Scheme == string(HTTPS) && appURL.Port() == "443") {
|
||||
appURL.Host = appURL.Hostname()
|
||||
}
|
||||
// This should be TrimRight to ensure that there is only a single '/' at the end of AppURL.
|
||||
AppURL = strings.TrimRight(appURL.String(), "/") + "/"
|
||||
|
||||
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
|
||||
// This value is empty if site does not have sub-url.
|
||||
AppSubURL = strings.TrimSuffix(appURL.Path, "/")
|
||||
StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
|
||||
|
||||
// Check if Domain differs from AppURL domain than update it to AppURL's domain
|
||||
urlHostname := appURL.Hostname()
|
||||
if urlHostname != Domain && net.ParseIP(urlHostname) == nil && urlHostname != "" {
|
||||
Domain = urlHostname
|
||||
}
|
||||
|
||||
AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix)
|
||||
AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed)
|
||||
|
||||
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
|
||||
ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes)
|
||||
|
||||
var defaultLocalURL string
|
||||
switch Protocol {
|
||||
case HTTPUnix:
|
||||
defaultLocalURL = "http://unix/"
|
||||
case FCGI:
|
||||
defaultLocalURL = AppURL
|
||||
case FCGIUnix:
|
||||
defaultLocalURL = AppURL
|
||||
default:
|
||||
defaultLocalURL = string(Protocol) + "://"
|
||||
if HTTPAddr == "0.0.0.0" {
|
||||
defaultLocalURL += net.JoinHostPort("localhost", HTTPPort) + "/"
|
||||
} else {
|
||||
defaultLocalURL += net.JoinHostPort(HTTPAddr, HTTPPort) + "/"
|
||||
}
|
||||
}
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
|
||||
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
|
||||
LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
|
||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||
RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
Log.DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
|
||||
if len(StaticRootPath) == 0 {
|
||||
StaticRootPath = AppWorkPath
|
||||
}
|
||||
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
|
||||
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
|
||||
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
|
||||
if !filepath.IsAbs(AppDataPath) {
|
||||
log.Info("The provided APP_DATA_PATH: %s is not absolute - it will be made absolute against the work path: %s", AppDataPath, AppWorkPath)
|
||||
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
|
||||
}
|
||||
|
||||
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
|
||||
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
|
||||
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
|
||||
if !filepath.IsAbs(PprofDataPath) {
|
||||
PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
|
||||
}
|
||||
|
||||
landingPage := sec.Key("LANDING_PAGE").MustString("home")
|
||||
switch landingPage {
|
||||
case "explore":
|
||||
LandingPageURL = LandingPageExplore
|
||||
case "organizations":
|
||||
LandingPageURL = LandingPageOrganizations
|
||||
case "login":
|
||||
LandingPageURL = LandingPageLogin
|
||||
case "":
|
||||
case "home":
|
||||
LandingPageURL = LandingPageHome
|
||||
default:
|
||||
LandingPageURL = LandingPage(landingPage)
|
||||
}
|
||||
|
||||
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err)
|
||||
}
|
||||
}
|
@ -12,15 +12,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// enumerates all the types of captchas
|
||||
const (
|
||||
ImageCaptcha = "image"
|
||||
ReCaptcha = "recaptcha"
|
||||
HCaptcha = "hcaptcha"
|
||||
MCaptcha = "mcaptcha"
|
||||
CfTurnstile = "cfturnstile"
|
||||
)
|
||||
|
||||
// Service settings
|
||||
var Service = struct {
|
||||
DefaultUserVisibility string
|
||||
@ -114,8 +105,8 @@ func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) {
|
||||
return result
|
||||
}
|
||||
|
||||
func loadServiceFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("service")
|
||||
func newService() {
|
||||
sec := Cfg.Section("service")
|
||||
Service.ActiveCodeLives = sec.Key("ACTIVE_CODE_LIVE_MINUTES").MustInt(180)
|
||||
Service.ResetPwdCodeLives = sec.Key("RESET_PASSWD_CODE_LIVE_MINUTES").MustInt(180)
|
||||
Service.DisableRegistration = sec.Key("DISABLE_REGISTRATION").MustBool()
|
||||
@ -193,13 +184,11 @@ func loadServiceFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
Service.ValidSiteURLSchemes = schemes
|
||||
|
||||
mustMapSetting(rootCfg, "service.explore", &Service.Explore)
|
||||
if err := Cfg.Section("service.explore").MapTo(&Service.Explore); err != nil {
|
||||
log.Fatal("Failed to map service.explore settings: %v", err)
|
||||
}
|
||||
|
||||
loadOpenIDSetting(rootCfg)
|
||||
}
|
||||
|
||||
func loadOpenIDSetting(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("openid")
|
||||
sec = Cfg.Section("openid")
|
||||
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
|
||||
Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn)
|
||||
pats := sec.Key("WHITELISTED_URIS").Strings(" ")
|
||||
|
@ -15,8 +15,7 @@ import (
|
||||
|
||||
// SessionConfig defines Session settings
|
||||
var SessionConfig = struct {
|
||||
OriginalProvider string
|
||||
Provider string
|
||||
Provider string
|
||||
// Provider configuration, it's corresponding to provider.
|
||||
ProviderConfig string
|
||||
// Cookie name to save session ID. Default is "MacaronSession".
|
||||
@ -40,8 +39,8 @@ var SessionConfig = struct {
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
}
|
||||
|
||||
func loadSessionFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("session")
|
||||
func newSessionService() {
|
||||
sec := Cfg.Section("session")
|
||||
SessionConfig.Provider = sec.Key("PROVIDER").In("memory",
|
||||
[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "db"})
|
||||
SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
|
||||
@ -68,7 +67,6 @@ func loadSessionFrom(rootCfg ConfigProvider) {
|
||||
log.Fatal("Can't shadow session config: %v", err)
|
||||
}
|
||||
SessionConfig.ProviderConfig = string(shadowConfig)
|
||||
SessionConfig.OriginalProvider = SessionConfig.Provider
|
||||
SessionConfig.Provider = "VirtualSession"
|
||||
|
||||
log.Info("Session Service Enabled")
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,197 +0,0 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
var SSH = struct {
|
||||
Disabled bool `ini:"DISABLE_SSH"`
|
||||
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
|
||||
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
|
||||
UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
|
||||
Domain string `ini:"SSH_DOMAIN"`
|
||||
Port int `ini:"SSH_PORT"`
|
||||
User string `ini:"SSH_USER"`
|
||||
ListenHost string `ini:"SSH_LISTEN_HOST"`
|
||||
ListenPort int `ini:"SSH_LISTEN_PORT"`
|
||||
RootPath string `ini:"SSH_ROOT_PATH"`
|
||||
ServerCiphers []string `ini:"SSH_SERVER_CIPHERS"`
|
||||
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
|
||||
ServerMACs []string `ini:"SSH_SERVER_MACS"`
|
||||
ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
|
||||
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
|
||||
KeygenPath string `ini:"SSH_KEYGEN_PATH"`
|
||||
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
|
||||
AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
|
||||
AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
|
||||
AuthorizedKeysCommandTemplateTemplate *template.Template `ini:"-"`
|
||||
MinimumKeySizeCheck bool `ini:"-"`
|
||||
MinimumKeySizes map[string]int `ini:"-"`
|
||||
CreateAuthorizedKeysFile bool `ini:"SSH_CREATE_AUTHORIZED_KEYS_FILE"`
|
||||
CreateAuthorizedPrincipalsFile bool `ini:"SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE"`
|
||||
ExposeAnonymous bool `ini:"SSH_EXPOSE_ANONYMOUS"`
|
||||
AuthorizedPrincipalsAllow []string `ini:"SSH_AUTHORIZED_PRINCIPALS_ALLOW"`
|
||||
AuthorizedPrincipalsEnabled bool `ini:"-"`
|
||||
TrustedUserCAKeys []string `ini:"SSH_TRUSTED_USER_CA_KEYS"`
|
||||
TrustedUserCAKeysFile string `ini:"SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
|
||||
TrustedUserCAKeysParsed []gossh.PublicKey `ini:"-"`
|
||||
PerWriteTimeout time.Duration `ini:"SSH_PER_WRITE_TIMEOUT"`
|
||||
PerWritePerKbTimeout time.Duration `ini:"SSH_PER_WRITE_PER_KB_TIMEOUT"`
|
||||
}{
|
||||
Disabled: false,
|
||||
StartBuiltinServer: false,
|
||||
Domain: "",
|
||||
Port: 22,
|
||||
ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"},
|
||||
ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"},
|
||||
ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
|
||||
KeygenPath: "ssh-keygen",
|
||||
MinimumKeySizeCheck: true,
|
||||
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047},
|
||||
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
|
||||
AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
|
||||
PerWriteTimeout: PerWriteTimeout,
|
||||
PerWritePerKbTimeout: PerWritePerKbTimeout,
|
||||
}
|
||||
|
||||
func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
|
||||
anything := false
|
||||
email := false
|
||||
username := false
|
||||
for _, value := range values {
|
||||
v := strings.ToLower(strings.TrimSpace(value))
|
||||
switch v {
|
||||
case "off":
|
||||
return []string{"off"}, false
|
||||
case "email":
|
||||
email = true
|
||||
case "username":
|
||||
username = true
|
||||
case "anything":
|
||||
anything = true
|
||||
}
|
||||
}
|
||||
if anything {
|
||||
return []string{"anything"}, true
|
||||
}
|
||||
|
||||
authorizedPrincipalsAllow := []string{}
|
||||
if username {
|
||||
authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "username")
|
||||
}
|
||||
if email {
|
||||
authorizedPrincipalsAllow = append(authorizedPrincipalsAllow, "email")
|
||||
}
|
||||
|
||||
return authorizedPrincipalsAllow, true
|
||||
}
|
||||
|
||||
func loadSSHFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("server")
|
||||
if len(SSH.Domain) == 0 {
|
||||
SSH.Domain = Domain
|
||||
}
|
||||
|
||||
homeDir, err := util.HomeDir()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to get home directory: %v", err)
|
||||
}
|
||||
homeDir = strings.ReplaceAll(homeDir, "\\", "/")
|
||||
|
||||
SSH.RootPath = path.Join(homeDir, ".ssh")
|
||||
serverCiphers := sec.Key("SSH_SERVER_CIPHERS").Strings(",")
|
||||
if len(serverCiphers) > 0 {
|
||||
SSH.ServerCiphers = serverCiphers
|
||||
}
|
||||
serverKeyExchanges := sec.Key("SSH_SERVER_KEY_EXCHANGES").Strings(",")
|
||||
if len(serverKeyExchanges) > 0 {
|
||||
SSH.ServerKeyExchanges = serverKeyExchanges
|
||||
}
|
||||
serverMACs := sec.Key("SSH_SERVER_MACS").Strings(",")
|
||||
if len(serverMACs) > 0 {
|
||||
SSH.ServerMACs = serverMACs
|
||||
}
|
||||
SSH.KeyTestPath = os.TempDir()
|
||||
if err = sec.MapTo(&SSH); err != nil {
|
||||
log.Fatal("Failed to map SSH settings: %v", err)
|
||||
}
|
||||
for i, key := range SSH.ServerHostKeys {
|
||||
if !filepath.IsAbs(key) {
|
||||
SSH.ServerHostKeys[i] = filepath.Join(AppDataPath, key)
|
||||
}
|
||||
}
|
||||
|
||||
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
|
||||
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
|
||||
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
|
||||
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
|
||||
|
||||
// When disable SSH, start builtin server value is ignored.
|
||||
if SSH.Disabled {
|
||||
SSH.StartBuiltinServer = false
|
||||
}
|
||||
|
||||
SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
|
||||
|
||||
for _, caKey := range SSH.TrustedUserCAKeys {
|
||||
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
|
||||
}
|
||||
|
||||
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
|
||||
}
|
||||
if len(SSH.TrustedUserCAKeys) > 0 {
|
||||
// Set the default as email,username otherwise we can leave it empty
|
||||
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
|
||||
} else {
|
||||
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("off")
|
||||
}
|
||||
|
||||
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
||||
|
||||
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
|
||||
minimumKeySizes := rootCfg.Section("ssh.minimum_key_sizes").Keys()
|
||||
for _, key := range minimumKeySizes {
|
||||
if key.MustInt() != -1 {
|
||||
SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt()
|
||||
} else {
|
||||
delete(SSH.MinimumKeySizes, strings.ToLower(key.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true)
|
||||
SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true)
|
||||
|
||||
SSH.AuthorizedPrincipalsBackup = false
|
||||
SSH.CreateAuthorizedPrincipalsFile = false
|
||||
if SSH.AuthorizedPrincipalsEnabled {
|
||||
SSH.AuthorizedPrincipalsBackup = sec.Key("SSH_AUTHORIZED_PRINCIPALS_BACKUP").MustBool(true)
|
||||
SSH.CreateAuthorizedPrincipalsFile = sec.Key("SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE").MustBool(true)
|
||||
}
|
||||
|
||||
SSH.ExposeAnonymous = sec.Key("SSH_EXPOSE_ANONYMOUS").MustBool(false)
|
||||
SSH.AuthorizedKeysCommandTemplate = sec.Key("SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE").MustString(SSH.AuthorizedKeysCommandTemplate)
|
||||
|
||||
SSH.AuthorizedKeysCommandTemplateTemplate = template.Must(template.New("").Parse(SSH.AuthorizedKeysCommandTemplate))
|
||||
|
||||
SSH.PerWriteTimeout = sec.Key("SSH_PER_WRITE_TIMEOUT").MustDuration(PerWriteTimeout)
|
||||
SSH.PerWritePerKbTimeout = sec.Key("SSH_PER_WRITE_PER_KB_TIMEOUT").MustDuration(PerWritePerKbTimeout)
|
||||
|
||||
// ensure parseRunModeSetting has been executed before this
|
||||
SSH.BuiltinServerUser = rootCfg.Section("server").Key("BUILTIN_SSH_SERVER_USER").MustString(RunUser)
|
||||
SSH.User = rootCfg.Section("server").Key("SSH_USER").MustString(SSH.BuiltinServerUser)
|
||||
}
|
@ -30,9 +30,9 @@ func (s *Storage) MapTo(v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section) Storage {
|
||||
func getStorage(name, typ string, targetSec *ini.Section) Storage {
|
||||
const sectionName = "storage"
|
||||
sec := rootCfg.Section(sectionName)
|
||||
sec := Cfg.Section(sectionName)
|
||||
|
||||
// Global Defaults
|
||||
sec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
|
||||
@ -43,7 +43,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section
|
||||
sec.Key("MINIO_USE_SSL").MustBool(false)
|
||||
|
||||
if targetSec == nil {
|
||||
targetSec, _ = rootCfg.NewSection(name)
|
||||
targetSec, _ = Cfg.NewSection(name)
|
||||
}
|
||||
|
||||
var storage Storage
|
||||
@ -51,12 +51,12 @@ func getStorage(rootCfg ConfigProvider, name, typ string, targetSec *ini.Section
|
||||
storage.Type = typ
|
||||
|
||||
overrides := make([]*ini.Section, 0, 3)
|
||||
nameSec, err := rootCfg.GetSection(sectionName + "." + name)
|
||||
nameSec, err := Cfg.GetSection(sectionName + "." + name)
|
||||
if err == nil {
|
||||
overrides = append(overrides, nameSec)
|
||||
}
|
||||
|
||||
typeSec, err := rootCfg.GetSection(sectionName + "." + typ)
|
||||
typeSec, err := Cfg.GetSection(sectionName + "." + typ)
|
||||
if err == nil {
|
||||
overrides = append(overrides, typeSec)
|
||||
nextType := typeSec.Key("STORAGE_TYPE").String()
|
||||
|
@ -20,12 +20,11 @@ MINIO_BUCKET = gitea-attachment
|
||||
STORAGE_TYPE = minio
|
||||
MINIO_ENDPOINT = my_minio:9000
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String())
|
||||
@ -43,12 +42,11 @@ MINIO_BUCKET = gitea-attachment
|
||||
[storage.minio]
|
||||
MINIO_BUCKET = gitea
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
||||
@ -65,12 +63,11 @@ MINIO_BUCKET = gitea-minio
|
||||
[storage]
|
||||
MINIO_BUCKET = gitea
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String())
|
||||
@ -88,24 +85,22 @@ MINIO_BUCKET = gitea
|
||||
[storage]
|
||||
STORAGE_TYPE = local
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
|
||||
func Test_getStorageGetDefaults(t *testing.T) {
|
||||
cfg, err := ini.Load([]byte(""))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(""))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
@ -121,27 +116,26 @@ MINIO_BUCKET = gitea-attachment
|
||||
[storage]
|
||||
MINIO_BUCKET = gitea-storage
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
{
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
{
|
||||
sec := cfg.Section("lfs")
|
||||
sec := Cfg.Section("lfs")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "lfs", storageType, sec)
|
||||
storage := getStorage("lfs", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
{
|
||||
sec := cfg.Section("avatar")
|
||||
sec := Cfg.Section("avatar")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "avatars", storageType, sec)
|
||||
storage := getStorage("avatars", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
@ -155,20 +149,19 @@ STORAGE_TYPE = lfs
|
||||
[storage.lfs]
|
||||
MINIO_BUCKET = gitea-storage
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
{
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
{
|
||||
sec := cfg.Section("lfs")
|
||||
sec := Cfg.Section("lfs")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "lfs", storageType, sec)
|
||||
storage := getStorage("lfs", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String())
|
||||
}
|
||||
@ -179,12 +172,11 @@ func Test_getStorageInheritStorageType(t *testing.T) {
|
||||
[storage]
|
||||
STORAGE_TYPE = minio
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
}
|
||||
@ -194,12 +186,11 @@ func Test_getStorageInheritNameSectionType(t *testing.T) {
|
||||
[storage.attachments]
|
||||
STORAGE_TYPE = minio
|
||||
`
|
||||
cfg, err := ini.Load([]byte(iniStr))
|
||||
assert.NoError(t, err)
|
||||
Cfg, _ = ini.Load([]byte(iniStr))
|
||||
|
||||
sec := cfg.Section("attachment")
|
||||
sec := Cfg.Section("attachment")
|
||||
storageType := sec.Key("STORAGE_TYPE").MustString("")
|
||||
storage := getStorage(cfg, "attachments", storageType, sec)
|
||||
storage := getStorage("attachments", storageType, sec)
|
||||
|
||||
assert.EqualValues(t, "minio", storage.Type)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user