code.gitea.io/gitea@v1.22.3/routers/web/admin/config.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2019 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package admin
     6  
     7  import (
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  	"strings"
    12  
    13  	system_model "code.gitea.io/gitea/models/system"
    14  	"code.gitea.io/gitea/modules/base"
    15  	"code.gitea.io/gitea/modules/git"
    16  	"code.gitea.io/gitea/modules/json"
    17  	"code.gitea.io/gitea/modules/log"
    18  	"code.gitea.io/gitea/modules/setting"
    19  	"code.gitea.io/gitea/modules/setting/config"
    20  	"code.gitea.io/gitea/modules/util"
    21  	"code.gitea.io/gitea/services/context"
    22  	"code.gitea.io/gitea/services/mailer"
    23  
    24  	"gitea.com/go-chi/session"
    25  )
    26  
    27  const (
    28  	tplConfig         base.TplName = "admin/config"
    29  	tplConfigSettings base.TplName = "admin/config_settings"
    30  )
    31  
    32  // SendTestMail send test mail to confirm mail service is OK
    33  func SendTestMail(ctx *context.Context) {
    34  	email := ctx.FormString("email")
    35  	// Send a test email to the user's email address and redirect back to Config
    36  	if err := mailer.SendTestMail(email); err != nil {
    37  		ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err))
    38  	} else {
    39  		ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
    40  	}
    41  
    42  	ctx.Redirect(setting.AppSubURL + "/admin/config")
    43  }
    44  
    45  func shadowPasswordKV(cfgItem, splitter string) string {
    46  	fields := strings.Split(cfgItem, splitter)
    47  	for i := 0; i < len(fields); i++ {
    48  		if strings.HasPrefix(fields[i], "password=") {
    49  			fields[i] = "password=******"
    50  			break
    51  		}
    52  	}
    53  	return strings.Join(fields, splitter)
    54  }
    55  
    56  func shadowURL(provider, cfgItem string) string {
    57  	u, err := url.Parse(cfgItem)
    58  	if err != nil {
    59  		log.Error("Shadowing Password for %v failed: %v", provider, err)
    60  		return cfgItem
    61  	}
    62  	if u.User != nil {
    63  		atIdx := strings.Index(cfgItem, "@")
    64  		if atIdx > 0 {
    65  			colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
    66  			if colonIdx > 0 {
    67  				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
    68  			}
    69  		}
    70  	}
    71  	return cfgItem
    72  }
    73  
    74  func shadowPassword(provider, cfgItem string) string {
    75  	switch provider {
    76  	case "redis":
    77  		return shadowPasswordKV(cfgItem, ",")
    78  	case "mysql":
    79  		// root:@tcp(localhost:3306)/macaron?charset=utf8
    80  		atIdx := strings.Index(cfgItem, "@")
    81  		if atIdx > 0 {
    82  			colonIdx := strings.Index(cfgItem[:atIdx], ":")
    83  			if colonIdx > 0 {
    84  				return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
    85  			}
    86  		}
    87  		return cfgItem
    88  	case "postgres":
    89  		// user=jiahuachen dbname=macaron port=5432 sslmode=disable
    90  		if !strings.HasPrefix(cfgItem, "postgres://") {
    91  			return shadowPasswordKV(cfgItem, " ")
    92  		}
    93  		fallthrough
    94  	case "couchbase":
    95  		return shadowURL(provider, cfgItem)
    96  		// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
    97  		// Notice: use shadowURL
    98  	}
    99  	return cfgItem
   100  }
   101  
   102  // Config show admin config page
   103  func Config(ctx *context.Context) {
   104  	ctx.Data["Title"] = ctx.Tr("admin.config_summary")
   105  	ctx.Data["PageIsAdminConfig"] = true
   106  	ctx.Data["PageIsAdminConfigSummary"] = true
   107  
   108  	ctx.Data["CustomConf"] = setting.CustomConf
   109  	ctx.Data["AppUrl"] = setting.AppURL
   110  	ctx.Data["AppBuiltWith"] = setting.AppBuiltWith
   111  	ctx.Data["Domain"] = setting.Domain
   112  	ctx.Data["OfflineMode"] = setting.OfflineMode
   113  	ctx.Data["RunUser"] = setting.RunUser
   114  	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
   115  	ctx.Data["GitVersion"] = git.DefaultFeatures().VersionInfo()
   116  
   117  	ctx.Data["AppDataPath"] = setting.AppDataPath
   118  	ctx.Data["RepoRootPath"] = setting.RepoRootPath
   119  	ctx.Data["CustomRootPath"] = setting.CustomPath
   120  	ctx.Data["LogRootPath"] = setting.Log.RootPath
   121  	ctx.Data["ScriptType"] = setting.ScriptType
   122  	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
   123  	ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
   124  
   125  	ctx.Data["SSH"] = setting.SSH
   126  	ctx.Data["LFS"] = setting.LFS
   127  
   128  	ctx.Data["Service"] = setting.Service
   129  	ctx.Data["DbCfg"] = setting.Database
   130  	ctx.Data["Webhook"] = setting.Webhook
   131  
   132  	ctx.Data["MailerEnabled"] = false
   133  	if setting.MailService != nil {
   134  		ctx.Data["MailerEnabled"] = true
   135  		ctx.Data["Mailer"] = setting.MailService
   136  	}
   137  
   138  	ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
   139  	ctx.Data["CacheInterval"] = setting.CacheService.Interval
   140  
   141  	ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
   142  	ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
   143  
   144  	sessionCfg := setting.SessionConfig
   145  	if sessionCfg.Provider == "VirtualSession" {
   146  		var realSession session.Options
   147  		if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
   148  			log.Error("Unable to unmarshall session config for virtual provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
   149  		}
   150  		sessionCfg.Provider = realSession.Provider
   151  		sessionCfg.ProviderConfig = realSession.ProviderConfig
   152  		sessionCfg.CookieName = realSession.CookieName
   153  		sessionCfg.CookiePath = realSession.CookiePath
   154  		sessionCfg.Gclifetime = realSession.Gclifetime
   155  		sessionCfg.Maxlifetime = realSession.Maxlifetime
   156  		sessionCfg.Secure = realSession.Secure
   157  		sessionCfg.Domain = realSession.Domain
   158  	}
   159  	sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
   160  	ctx.Data["SessionConfig"] = sessionCfg
   161  
   162  	ctx.Data["Git"] = setting.Git
   163  	ctx.Data["AccessLogTemplate"] = setting.Log.AccessLogTemplate
   164  	ctx.Data["LogSQL"] = setting.Database.LogSQL
   165  
   166  	ctx.Data["Loggers"] = log.GetManager().DumpLoggers()
   167  	config.GetDynGetter().InvalidateCache()
   168  	prepareStartupProblemsAlert(ctx)
   169  
   170  	ctx.HTML(http.StatusOK, tplConfig)
   171  }
   172  
   173  func ConfigSettings(ctx *context.Context) {
   174  	ctx.Data["Title"] = ctx.Tr("admin.config_settings")
   175  	ctx.Data["PageIsAdminConfig"] = true
   176  	ctx.Data["PageIsAdminConfigSettings"] = true
   177  	ctx.Data["DefaultOpenWithEditorAppsString"] = setting.DefaultOpenWithEditorApps().ToTextareaString()
   178  	ctx.HTML(http.StatusOK, tplConfigSettings)
   179  }
   180  
   181  func ChangeConfig(ctx *context.Context) {
   182  	key := strings.TrimSpace(ctx.FormString("key"))
   183  	value := ctx.FormString("value")
   184  	cfg := setting.Config()
   185  
   186  	marshalBool := func(v string) (string, error) {
   187  		if b, _ := strconv.ParseBool(v); b {
   188  			return "true", nil
   189  		}
   190  		return "false", nil
   191  	}
   192  	marshalOpenWithApps := func(value string) (string, error) {
   193  		lines := strings.Split(value, "\n")
   194  		var openWithEditorApps setting.OpenWithEditorAppsType
   195  		for _, line := range lines {
   196  			line = strings.TrimSpace(line)
   197  			if line == "" {
   198  				continue
   199  			}
   200  			displayName, openURL, ok := strings.Cut(line, "=")
   201  			displayName, openURL = strings.TrimSpace(displayName), strings.TrimSpace(openURL)
   202  			if !ok || displayName == "" || openURL == "" {
   203  				continue
   204  			}
   205  			openWithEditorApps = append(openWithEditorApps, setting.OpenWithEditorApp{
   206  				DisplayName: strings.TrimSpace(displayName),
   207  				OpenURL:     strings.TrimSpace(openURL),
   208  			})
   209  		}
   210  		b, err := json.Marshal(openWithEditorApps)
   211  		if err != nil {
   212  			return "", err
   213  		}
   214  		return string(b), nil
   215  	}
   216  	marshallers := map[string]func(string) (string, error){
   217  		cfg.Picture.DisableGravatar.DynKey():       marshalBool,
   218  		cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool,
   219  		cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps,
   220  	}
   221  	marshaller, hasMarshaller := marshallers[key]
   222  	if !hasMarshaller {
   223  		ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
   224  		return
   225  	}
   226  	marshaledValue, err := marshaller(value)
   227  	if err != nil {
   228  		ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
   229  		return
   230  	}
   231  	if err = system_model.SetSettings(ctx, map[string]string{key: marshaledValue}); err != nil {
   232  		ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
   233  		return
   234  	}
   235  
   236  	config.GetDynGetter().InvalidateCache()
   237  	ctx.JSONOK()
   238  }