From 161e12e157a48f9bcd2de50c187c77444aa2a9a8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 27 Jun 2019 00:12:38 +0800 Subject: [PATCH] Shadow the password on cache and session config on admin panel (#7300) * shadow the password on cache and session config on admin panel * add shadow password of mysql/postgres/couchbase * fix log import --- routers/admin/admin.go | 68 ++++++++++++++++++++++++++++++++++-- routers/admin/admin_test.go | 69 +++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 routers/admin/admin_test.go diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 2836b7ddc..370f81652 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -6,6 +7,7 @@ package admin import ( "fmt" + "net/url" "os" "runtime" "strings" @@ -19,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/cron" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" ) @@ -202,6 +205,63 @@ func SendTestMail(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/admin/config") } +func shadownPasswordKV(cfgItem, splitter string) string { + fields := strings.Split(cfgItem, splitter) + for i := 0; i < len(fields); i++ { + if strings.HasPrefix(fields[i], "password=") { + fields[i] = "password=******" + break + } + } + return strings.Join(fields, splitter) +} + +func shadownURL(provider, cfgItem string) string { + u, err := url.Parse(cfgItem) + if err != nil { + log.Error("shodowPassword %v failed: %v", provider, err) + return cfgItem + } + if u.User != nil { + atIdx := strings.Index(cfgItem, "@") + if atIdx > 0 { + colonIdx := strings.LastIndex(cfgItem[:atIdx], ":") + if colonIdx > 0 { + return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] + } + } + } + return cfgItem +} + +func shadowPassword(provider, cfgItem string) string { + switch provider { + case "redis": + return shadownPasswordKV(cfgItem, ",") + case "mysql": + //root:@tcp(localhost:3306)/macaron?charset=utf8 + atIdx := strings.Index(cfgItem, "@") + if atIdx > 0 { + colonIdx := strings.Index(cfgItem[:atIdx], ":") + if colonIdx > 0 { + return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:] + } + } + return cfgItem + case "postgres": + // user=jiahuachen dbname=macaron port=5432 sslmode=disable + if !strings.HasPrefix(cfgItem, "postgres://") { + return shadownPasswordKV(cfgItem, " ") + } + + // postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full + // Notice: use shadwonURL + } + + // "couchbase" + return shadownURL(provider, cfgItem) +} + // Config show admin config page func Config(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.config") @@ -239,10 +299,14 @@ func Config(ctx *context.Context) { ctx.Data["CacheAdapter"] = setting.CacheService.Adapter ctx.Data["CacheInterval"] = setting.CacheService.Interval - ctx.Data["CacheConn"] = setting.CacheService.Conn + + ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn) ctx.Data["CacheItemTTL"] = setting.CacheService.TTL - ctx.Data["SessionConfig"] = setting.SessionConfig + sessionCfg := setting.SessionConfig + sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig) + + ctx.Data["SessionConfig"] = sessionCfg ctx.Data["DisableGravatar"] = setting.DisableGravatar ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar diff --git a/routers/admin/admin_test.go b/routers/admin/admin_test.go new file mode 100644 index 000000000..da404e50d --- /dev/null +++ b/routers/admin/admin_test.go @@ -0,0 +1,69 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package admin + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestShadowPassword(t *testing.T) { + var kases = []struct { + Provider string + CfgItem string + Result string + }{ + { + Provider: "redis", + CfgItem: "network=tcp,addr=:6379,password=gitea,db=0,pool_size=100,idle_timeout=180", + Result: "network=tcp,addr=:6379,password=******,db=0,pool_size=100,idle_timeout=180", + }, + { + Provider: "mysql", + CfgItem: "root:@tcp(localhost:3306)/gitea?charset=utf8", + Result: "root:******@tcp(localhost:3306)/gitea?charset=utf8", + }, + { + Provider: "mysql", + CfgItem: "/gitea?charset=utf8", + Result: "/gitea?charset=utf8", + }, + { + Provider: "mysql", + CfgItem: "user:mypassword@/dbname", + Result: "user:******@/dbname", + }, + { + Provider: "postgres", + CfgItem: "user=pqgotest dbname=pqgotest sslmode=verify-full", + Result: "user=pqgotest dbname=pqgotest sslmode=verify-full", + }, + { + Provider: "postgres", + CfgItem: "user=pqgotest password= dbname=pqgotest sslmode=verify-full", + Result: "user=pqgotest password=****** dbname=pqgotest sslmode=verify-full", + }, + { + Provider: "postgres", + CfgItem: "postgres://user:pass@hostname/dbname", + Result: "postgres://user:******@hostname/dbname", + }, + { + Provider: "couchbase", + CfgItem: "http://dev-couchbase.example.com:8091/", + Result: "http://dev-couchbase.example.com:8091/", + }, + { + Provider: "couchbase", + CfgItem: "http://user:the_password@dev-couchbase.example.com:8091/", + Result: "http://user:******@dev-couchbase.example.com:8091/", + }, + } + + for _, k := range kases { + assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem)) + } +}