mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
13 KiB
294 lines
13 KiB
// Copyright 2019 The Gitea Authors. All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package setting |
|
|
|
import ( |
|
"regexp" |
|
"runtime" |
|
"strings" |
|
"time" |
|
|
|
"code.gitea.io/gitea/modules/glob" |
|
"code.gitea.io/gitea/modules/log" |
|
"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 |
|
DefaultUserVisibilityMode structs.VisibleType |
|
AllowedUserVisibilityModes []string |
|
AllowedUserVisibilityModesSlice AllowedVisibility `ini:"-"` |
|
DefaultOrgVisibility string |
|
DefaultOrgVisibilityMode structs.VisibleType |
|
ActiveCodeLives int |
|
ResetPwdCodeLives int |
|
RegisterEmailConfirm bool |
|
RegisterManualConfirm bool |
|
EmailDomainAllowList []glob.Glob |
|
EmailDomainBlockList []glob.Glob |
|
DisableRegistration bool |
|
AllowOnlyInternalRegistration bool |
|
AllowOnlyExternalRegistration bool |
|
ShowRegistrationButton bool |
|
EnablePasswordSignInForm bool |
|
ShowMilestonesDashboardPage bool |
|
RequireSignInViewStrict bool |
|
BlockAnonymousAccessExpensive bool |
|
EnableNotifyMail bool |
|
EnableBasicAuth bool |
|
EnablePasskeyAuth bool |
|
EnableReverseProxyAuth bool |
|
EnableReverseProxyAuthAPI bool |
|
EnableReverseProxyAutoRegister bool |
|
EnableReverseProxyEmail bool |
|
EnableReverseProxyFullName bool |
|
EnableCaptcha bool |
|
RequireCaptchaForLogin bool |
|
RequireExternalRegistrationCaptcha bool |
|
RequireExternalRegistrationPassword bool |
|
CaptchaType string |
|
RecaptchaSecret string |
|
RecaptchaSitekey string |
|
RecaptchaURL string |
|
CfTurnstileSecret string |
|
CfTurnstileSitekey string |
|
HcaptchaSecret string |
|
HcaptchaSitekey string |
|
McaptchaSecret string |
|
McaptchaSitekey string |
|
McaptchaURL string |
|
DefaultKeepEmailPrivate bool |
|
DefaultAllowCreateOrganization bool |
|
DefaultUserIsRestricted bool |
|
EnableTimetracking bool |
|
DefaultEnableTimetracking bool |
|
DefaultEnableDependencies bool |
|
AllowCrossRepositoryDependencies bool |
|
DefaultAllowOnlyContributorsToTrackTime bool |
|
NoReplyAddress string |
|
UserLocationMapURL string |
|
EnableUserHeatmap bool |
|
AutoWatchNewRepos bool |
|
AutoWatchOnChanges bool |
|
DefaultOrgMemberVisible bool |
|
UserDeleteWithCommentsMaxTime time.Duration |
|
ValidSiteURLSchemes []string |
|
|
|
// OpenID settings |
|
EnableOpenIDSignIn bool |
|
EnableOpenIDSignUp bool |
|
OpenIDWhitelist []*regexp.Regexp |
|
OpenIDBlacklist []*regexp.Regexp |
|
|
|
// Explore page settings |
|
Explore struct { |
|
RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` |
|
DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` |
|
DisableOrganizationsPage bool `ini:"DISABLE_ORGANIZATIONS_PAGE"` |
|
DisableCodePage bool `ini:"DISABLE_CODE_PAGE"` |
|
} `ini:"service.explore"` |
|
|
|
QoS struct { |
|
Enabled bool |
|
MaxInFlightRequests int |
|
MaxWaitingRequests int |
|
TargetWaitTime time.Duration |
|
} |
|
}{ |
|
AllowedUserVisibilityModesSlice: []bool{true, true, true}, |
|
} |
|
|
|
// AllowedVisibility store in a 3 item bool array what is allowed |
|
type AllowedVisibility []bool |
|
|
|
// IsAllowedVisibility check if a AllowedVisibility allow a specific VisibleType |
|
func (a AllowedVisibility) IsAllowedVisibility(t structs.VisibleType) bool { |
|
if int(t) >= len(a) { |
|
return false |
|
} |
|
return a[t] |
|
} |
|
|
|
// ToVisibleTypeSlice convert a AllowedVisibility into a VisibleType slice |
|
func (a AllowedVisibility) ToVisibleTypeSlice() (result []structs.VisibleType) { |
|
for i, v := range a { |
|
if v { |
|
result = append(result, structs.VisibleType(i)) |
|
} |
|
} |
|
return result |
|
} |
|
|
|
func CompileEmailGlobList(sec ConfigSection, keys ...string) (globs []glob.Glob) { |
|
for _, key := range keys { |
|
list := sec.Key(key).Strings(",") |
|
for _, s := range list { |
|
if g, err := glob.Compile(s); err == nil { |
|
globs = append(globs, g) |
|
} else { |
|
log.Error("Skip invalid email allow/block list expression %q: %v", s, err) |
|
} |
|
} |
|
} |
|
return globs |
|
} |
|
|
|
func loadServiceFrom(rootCfg ConfigProvider) { |
|
sec := rootCfg.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() |
|
Service.AllowOnlyInternalRegistration = sec.Key("ALLOW_ONLY_INTERNAL_REGISTRATION").MustBool() |
|
Service.AllowOnlyExternalRegistration = sec.Key("ALLOW_ONLY_EXTERNAL_REGISTRATION").MustBool() |
|
if Service.AllowOnlyExternalRegistration && Service.AllowOnlyInternalRegistration { |
|
log.Warn("ALLOW_ONLY_INTERNAL_REGISTRATION and ALLOW_ONLY_EXTERNAL_REGISTRATION are true - disabling registration") |
|
Service.DisableRegistration = true |
|
} |
|
if !sec.Key("REGISTER_EMAIL_CONFIRM").MustBool() { |
|
Service.RegisterManualConfirm = sec.Key("REGISTER_MANUAL_CONFIRM").MustBool(false) |
|
} else { |
|
Service.RegisterManualConfirm = false |
|
} |
|
if sec.HasKey("EMAIL_DOMAIN_WHITELIST") { |
|
deprecatedSetting(rootCfg, "service", "EMAIL_DOMAIN_WHITELIST", "service", "EMAIL_DOMAIN_ALLOWLIST", "1.21") |
|
} |
|
Service.EmailDomainAllowList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_WHITELIST", "EMAIL_DOMAIN_ALLOWLIST") |
|
Service.EmailDomainBlockList = CompileEmailGlobList(sec, "EMAIL_DOMAIN_BLOCKLIST") |
|
Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration)) |
|
Service.ShowMilestonesDashboardPage = sec.Key("SHOW_MILESTONES_DASHBOARD_PAGE").MustBool(true) |
|
|
|
// boolean values are considered as "strict" |
|
var err error |
|
Service.RequireSignInViewStrict, err = sec.Key("REQUIRE_SIGNIN_VIEW").Bool() |
|
if s := sec.Key("REQUIRE_SIGNIN_VIEW").String(); err != nil && s != "" { |
|
// non-boolean value only supports "expensive" at the moment |
|
Service.BlockAnonymousAccessExpensive = s == "expensive" |
|
if !Service.BlockAnonymousAccessExpensive { |
|
log.Fatal("Invalid config option: REQUIRE_SIGNIN_VIEW = %s", s) |
|
} |
|
} |
|
|
|
Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true) |
|
Service.EnablePasswordSignInForm = sec.Key("ENABLE_PASSWORD_SIGNIN_FORM").MustBool(true) |
|
Service.EnablePasskeyAuth = sec.Key("ENABLE_PASSKEY_AUTHENTICATION").MustBool(true) |
|
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool() |
|
Service.EnableReverseProxyAuthAPI = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION_API").MustBool() |
|
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool() |
|
Service.EnableReverseProxyEmail = sec.Key("ENABLE_REVERSE_PROXY_EMAIL").MustBool() |
|
Service.EnableReverseProxyFullName = sec.Key("ENABLE_REVERSE_PROXY_FULL_NAME").MustBool() |
|
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false) |
|
Service.RequireCaptchaForLogin = sec.Key("REQUIRE_CAPTCHA_FOR_LOGIN").MustBool(false) |
|
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool(Service.EnableCaptcha) |
|
Service.RequireExternalRegistrationPassword = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_PASSWORD").MustBool() |
|
Service.CaptchaType = sec.Key("CAPTCHA_TYPE").MustString(ImageCaptcha) |
|
Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("") |
|
Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("") |
|
Service.RecaptchaURL = sec.Key("RECAPTCHA_URL").MustString("https://www.google.com/recaptcha/") |
|
Service.CfTurnstileSecret = sec.Key("CF_TURNSTILE_SECRET").MustString("") |
|
Service.CfTurnstileSitekey = sec.Key("CF_TURNSTILE_SITEKEY").MustString("") |
|
Service.HcaptchaSecret = sec.Key("HCAPTCHA_SECRET").MustString("") |
|
Service.HcaptchaSitekey = sec.Key("HCAPTCHA_SITEKEY").MustString("") |
|
Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/") |
|
Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("") |
|
Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("") |
|
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() |
|
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) |
|
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) |
|
Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true) |
|
if Service.EnableTimetracking { |
|
Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) |
|
} |
|
Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true) |
|
Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true) |
|
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true) |
|
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain) |
|
Service.UserLocationMapURL = sec.Key("USER_LOCATION_MAP_URL").String() |
|
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true) |
|
Service.AutoWatchNewRepos = sec.Key("AUTO_WATCH_NEW_REPOS").MustBool(true) |
|
Service.AutoWatchOnChanges = sec.Key("AUTO_WATCH_ON_CHANGES").MustBool(false) |
|
modes := sec.Key("ALLOWED_USER_VISIBILITY_MODES").Strings(",") |
|
if len(modes) != 0 { |
|
Service.AllowedUserVisibilityModes = []string{} |
|
Service.AllowedUserVisibilityModesSlice = []bool{false, false, false} |
|
for _, sMode := range modes { |
|
if tp, ok := structs.VisibilityModes[sMode]; ok { // remove unsupported modes |
|
Service.AllowedUserVisibilityModes = append(Service.AllowedUserVisibilityModes, sMode) |
|
Service.AllowedUserVisibilityModesSlice[tp] = true |
|
} else { |
|
log.Warn("ALLOWED_USER_VISIBILITY_MODES %s is unsupported", sMode) |
|
} |
|
} |
|
} |
|
|
|
if len(Service.AllowedUserVisibilityModes) == 0 { |
|
Service.AllowedUserVisibilityModes = []string{"public", "limited", "private"} |
|
Service.AllowedUserVisibilityModesSlice = []bool{true, true, true} |
|
} |
|
|
|
Service.DefaultUserVisibility = sec.Key("DEFAULT_USER_VISIBILITY").String() |
|
if Service.DefaultUserVisibility == "" { |
|
Service.DefaultUserVisibility = Service.AllowedUserVisibilityModes[0] |
|
} else if !Service.AllowedUserVisibilityModesSlice[structs.VisibilityModes[Service.DefaultUserVisibility]] { |
|
log.Warn("DEFAULT_USER_VISIBILITY %s is wrong or not in ALLOWED_USER_VISIBILITY_MODES, using first allowed", Service.DefaultUserVisibility) |
|
Service.DefaultUserVisibility = Service.AllowedUserVisibilityModes[0] |
|
} |
|
Service.DefaultUserVisibilityMode = structs.VisibilityModes[Service.DefaultUserVisibility] |
|
Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes)) |
|
Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility] |
|
Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool() |
|
Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0) |
|
sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https") |
|
Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",") |
|
schemes := make([]string, 0, len(Service.ValidSiteURLSchemes)) |
|
for _, scheme := range Service.ValidSiteURLSchemes { |
|
scheme = strings.ToLower(strings.TrimSpace(scheme)) |
|
if scheme != "" { |
|
schemes = append(schemes, scheme) |
|
} |
|
} |
|
Service.ValidSiteURLSchemes = schemes |
|
|
|
mustMapSetting(rootCfg, "service.explore", &Service.Explore) |
|
|
|
loadOpenIDSetting(rootCfg) |
|
loadQosSetting(rootCfg) |
|
} |
|
|
|
func loadOpenIDSetting(rootCfg ConfigProvider) { |
|
sec := rootCfg.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(" ") |
|
if len(pats) != 0 { |
|
Service.OpenIDWhitelist = make([]*regexp.Regexp, len(pats)) |
|
for i, p := range pats { |
|
Service.OpenIDWhitelist[i] = regexp.MustCompilePOSIX(p) |
|
} |
|
} |
|
pats = sec.Key("BLACKLISTED_URIS").Strings(" ") |
|
if len(pats) != 0 { |
|
Service.OpenIDBlacklist = make([]*regexp.Regexp, len(pats)) |
|
for i, p := range pats { |
|
Service.OpenIDBlacklist[i] = regexp.MustCompilePOSIX(p) |
|
} |
|
} |
|
} |
|
|
|
func loadQosSetting(rootCfg ConfigProvider) { |
|
sec := rootCfg.Section("qos") |
|
Service.QoS.Enabled = sec.Key("ENABLED").MustBool(false) |
|
Service.QoS.MaxInFlightRequests = sec.Key("MAX_INFLIGHT").MustInt(4 * runtime.NumCPU()) |
|
Service.QoS.MaxWaitingRequests = sec.Key("MAX_WAITING").MustInt(100) |
|
Service.QoS.TargetWaitTime = sec.Key("TARGET_WAIT_TIME").MustDuration(250 * time.Millisecond) |
|
}
|
|
|