github.com/mattermost/mattermost-server/v5@v5.39.3/config/diff.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package config 5 6 import ( 7 "fmt" 8 "reflect" 9 10 "github.com/mattermost/mattermost-server/v5/model" 11 ) 12 13 type ConfigDiffs []ConfigDiff 14 15 type ConfigDiff struct { 16 Path string `json:"path"` 17 BaseVal interface{} `json:"base_val"` 18 ActualVal interface{} `json:"actual_val"` 19 } 20 21 var configSensitivePaths = map[string]bool{ 22 "LdapSettings.BindPassword": true, 23 "FileSettings.PublicLinkSalt": true, 24 "FileSettings.AmazonS3SecretAccessKey": true, 25 "SqlSettings.DataSource": true, 26 "SqlSettings.AtRestEncryptKey": true, 27 "SqlSettings.DataSourceReplicas": true, 28 "SqlSettings.DataSourceSearchReplicas": true, 29 "EmailSettings.SMTPPassword": true, 30 "GitLabSettings.Secret": true, 31 "GoogleSettings.Secret": true, 32 "Office365Settings.Secret": true, 33 "OpenIdSettings.Secret": true, 34 "ElasticsearchSettings.Password": true, 35 "MessageExportSettings.GlobalRelaySettings.SmtpUsername": true, 36 "MessageExportSettings.GlobalRelaySettings.SmtpPassword": true, 37 "MessageExportSettings.GlobalRelaySettings.EmailAddress": true, 38 "ServiceSettings.GfycatApiSecret": true, 39 "ServiceSettings.SplitKey": true, 40 "PluginSettings.Plugins": true, 41 } 42 43 // Sanitize replaces sensitive config values in the diff with asterisks filled strings. 44 func (cd ConfigDiffs) Sanitize() ConfigDiffs { 45 if len(cd) == 1 { 46 cfgPtr, ok := cd[0].BaseVal.(*model.Config) 47 if ok { 48 cfgPtr.Sanitize() 49 } 50 cfgPtr, ok = cd[0].ActualVal.(*model.Config) 51 if ok { 52 cfgPtr.Sanitize() 53 } 54 cfgVal, ok := cd[0].BaseVal.(model.Config) 55 if ok { 56 cfgVal.Sanitize() 57 } 58 cfgVal, ok = cd[0].ActualVal.(model.Config) 59 if ok { 60 cfgVal.Sanitize() 61 } 62 } 63 64 for i := range cd { 65 if configSensitivePaths[cd[i].Path] { 66 cd[i].BaseVal = model.FAKE_SETTING 67 cd[i].ActualVal = model.FAKE_SETTING 68 } 69 } 70 71 return cd 72 } 73 74 func diff(base, actual reflect.Value, label string) ([]ConfigDiff, error) { 75 var diffs []ConfigDiff 76 77 if base.IsZero() && actual.IsZero() { 78 return diffs, nil 79 } 80 81 if base.IsZero() || actual.IsZero() { 82 return append(diffs, ConfigDiff{ 83 Path: label, 84 BaseVal: base.Interface(), 85 ActualVal: actual.Interface(), 86 }), nil 87 } 88 89 baseType := base.Type() 90 actualType := actual.Type() 91 92 if baseType.Kind() == reflect.Ptr { 93 base = reflect.Indirect(base) 94 actual = reflect.Indirect(actual) 95 baseType = base.Type() 96 actualType = actual.Type() 97 } 98 99 if baseType != actualType { 100 return nil, fmt.Errorf("not same type %s %s", baseType, actualType) 101 } 102 103 switch baseType.Kind() { 104 case reflect.Struct: 105 if base.NumField() != actual.NumField() { 106 return nil, fmt.Errorf("not same number of fields in struct") 107 } 108 for i := 0; i < base.NumField(); i++ { 109 fieldLabel := baseType.Field(i).Name 110 if label != "" { 111 fieldLabel = label + "." + fieldLabel 112 } 113 d, err := diff(base.Field(i), actual.Field(i), fieldLabel) 114 if err != nil { 115 return nil, err 116 } 117 diffs = append(diffs, d...) 118 } 119 default: 120 if !reflect.DeepEqual(base.Interface(), actual.Interface()) { 121 diffs = append(diffs, ConfigDiff{ 122 Path: label, 123 BaseVal: base.Interface(), 124 ActualVal: actual.Interface(), 125 }) 126 } 127 } 128 129 return diffs, nil 130 } 131 132 func Diff(base, actual *model.Config) (ConfigDiffs, error) { 133 if base == nil || actual == nil { 134 return nil, fmt.Errorf("input configs should not be nil") 135 } 136 baseVal := reflect.Indirect(reflect.ValueOf(base)) 137 actualVal := reflect.Indirect(reflect.ValueOf(actual)) 138 return diff(baseVal, actualVal, "") 139 } 140 141 func (cd ConfigDiffs) String() string { 142 return fmt.Sprintf("%+v", []ConfigDiff(cd)) 143 }