github.com/adacta-ru/mattermost-server/v6@v6.0.0/config/utils.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 "encoding/json" 8 "reflect" 9 "strings" 10 11 "github.com/adacta-ru/mattermost-server/v6/mlog" 12 "github.com/adacta-ru/mattermost-server/v6/model" 13 "github.com/adacta-ru/mattermost-server/v6/utils" 14 ) 15 16 // desanitize replaces fake settings with their actual values. 17 func desanitize(actual, target *model.Config) { 18 if target.LdapSettings.BindPassword != nil && *target.LdapSettings.BindPassword == model.FAKE_SETTING { 19 *target.LdapSettings.BindPassword = *actual.LdapSettings.BindPassword 20 } 21 22 if *target.FileSettings.PublicLinkSalt == model.FAKE_SETTING { 23 *target.FileSettings.PublicLinkSalt = *actual.FileSettings.PublicLinkSalt 24 } 25 if *target.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING { 26 target.FileSettings.AmazonS3SecretAccessKey = actual.FileSettings.AmazonS3SecretAccessKey 27 } 28 29 if *target.EmailSettings.SMTPPassword == model.FAKE_SETTING { 30 target.EmailSettings.SMTPPassword = actual.EmailSettings.SMTPPassword 31 } 32 33 if *target.GitLabSettings.Secret == model.FAKE_SETTING { 34 target.GitLabSettings.Secret = actual.GitLabSettings.Secret 35 } 36 37 if target.GoogleSettings.Secret != nil && *target.GoogleSettings.Secret == model.FAKE_SETTING { 38 target.GoogleSettings.Secret = actual.GoogleSettings.Secret 39 } 40 41 if target.Office365Settings.Secret != nil && *target.Office365Settings.Secret == model.FAKE_SETTING { 42 target.Office365Settings.Secret = actual.Office365Settings.Secret 43 } 44 45 if target.OpenIdSettings.Secret != nil && *target.OpenIdSettings.Secret == model.FAKE_SETTING { 46 target.OpenIdSettings.Secret = actual.OpenIdSettings.Secret 47 } 48 49 if *target.SqlSettings.DataSource == model.FAKE_SETTING { 50 *target.SqlSettings.DataSource = *actual.SqlSettings.DataSource 51 } 52 if *target.SqlSettings.AtRestEncryptKey == model.FAKE_SETTING { 53 target.SqlSettings.AtRestEncryptKey = actual.SqlSettings.AtRestEncryptKey 54 } 55 56 if *target.ElasticsearchSettings.Password == model.FAKE_SETTING { 57 *target.ElasticsearchSettings.Password = *actual.ElasticsearchSettings.Password 58 } 59 60 if len(target.SqlSettings.DataSourceReplicas) == len(actual.SqlSettings.DataSourceReplicas) { 61 for i, value := range target.SqlSettings.DataSourceReplicas { 62 if value == model.FAKE_SETTING { 63 target.SqlSettings.DataSourceReplicas[i] = actual.SqlSettings.DataSourceReplicas[i] 64 } 65 } 66 } 67 68 if len(target.SqlSettings.DataSourceSearchReplicas) == len(actual.SqlSettings.DataSourceSearchReplicas) { 69 for i, value := range target.SqlSettings.DataSourceSearchReplicas { 70 if value == model.FAKE_SETTING { 71 target.SqlSettings.DataSourceSearchReplicas[i] = actual.SqlSettings.DataSourceSearchReplicas[i] 72 } 73 } 74 } 75 76 if *target.MessageExportSettings.GlobalRelaySettings.SmtpPassword == model.FAKE_SETTING { 77 *target.MessageExportSettings.GlobalRelaySettings.SmtpPassword = *actual.MessageExportSettings.GlobalRelaySettings.SmtpPassword 78 } 79 80 if target.ServiceSettings.GfycatApiSecret != nil && *target.ServiceSettings.GfycatApiSecret == model.FAKE_SETTING { 81 *target.ServiceSettings.GfycatApiSecret = *actual.ServiceSettings.GfycatApiSecret 82 } 83 84 if *target.ServiceSettings.SplitKey == model.FAKE_SETTING { 85 *target.ServiceSettings.SplitKey = *actual.ServiceSettings.SplitKey 86 } 87 } 88 89 // fixConfig patches invalid or missing data in the configuration, returning true if changed. 90 func fixConfig(cfg *model.Config) bool { 91 changed := false 92 93 // Ensure SiteURL has no trailing slash. 94 if strings.HasSuffix(*cfg.ServiceSettings.SiteURL, "/") { 95 *cfg.ServiceSettings.SiteURL = strings.TrimRight(*cfg.ServiceSettings.SiteURL, "/") 96 changed = true 97 } 98 99 // Ensure the directory for a local file store has a trailing slash. 100 if *cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL { 101 if *cfg.FileSettings.Directory != "" && !strings.HasSuffix(*cfg.FileSettings.Directory, "/") { 102 *cfg.FileSettings.Directory += "/" 103 changed = true 104 } 105 } 106 107 if FixInvalidLocales(cfg) { 108 changed = true 109 } 110 111 return changed 112 } 113 114 // FixInvalidLocales checks and corrects the given config for invalid locale-related settings. 115 // 116 // Ideally, this function would be completely internal, but it's currently exposed to allow the cli 117 // to test the config change before allowing the save. 118 func FixInvalidLocales(cfg *model.Config) bool { 119 var changed bool 120 121 locales := utils.GetSupportedLocales() 122 if _, ok := locales[*cfg.LocalizationSettings.DefaultServerLocale]; !ok { 123 *cfg.LocalizationSettings.DefaultServerLocale = model.DEFAULT_LOCALE 124 mlog.Warn("DefaultServerLocale must be one of the supported locales. Setting DefaultServerLocale to en as default value.") 125 changed = true 126 } 127 128 if _, ok := locales[*cfg.LocalizationSettings.DefaultClientLocale]; !ok { 129 *cfg.LocalizationSettings.DefaultClientLocale = model.DEFAULT_LOCALE 130 mlog.Warn("DefaultClientLocale must be one of the supported locales. Setting DefaultClientLocale to en as default value.") 131 changed = true 132 } 133 134 if len(*cfg.LocalizationSettings.AvailableLocales) > 0 { 135 isDefaultClientLocaleInAvailableLocales := false 136 for _, word := range strings.Split(*cfg.LocalizationSettings.AvailableLocales, ",") { 137 if _, ok := locales[word]; !ok { 138 *cfg.LocalizationSettings.AvailableLocales = "" 139 isDefaultClientLocaleInAvailableLocales = true 140 mlog.Warn("AvailableLocales must include DefaultClientLocale. Setting AvailableLocales to all locales as default value.") 141 changed = true 142 break 143 } 144 145 if word == *cfg.LocalizationSettings.DefaultClientLocale { 146 isDefaultClientLocaleInAvailableLocales = true 147 } 148 } 149 150 availableLocales := *cfg.LocalizationSettings.AvailableLocales 151 152 if !isDefaultClientLocaleInAvailableLocales { 153 availableLocales += "," + *cfg.LocalizationSettings.DefaultClientLocale 154 mlog.Warn("Adding DefaultClientLocale to AvailableLocales.") 155 changed = true 156 } 157 158 *cfg.LocalizationSettings.AvailableLocales = strings.Join(utils.RemoveDuplicatesFromStringArray(strings.Split(availableLocales, ",")), ",") 159 } 160 161 return changed 162 } 163 164 // Merge merges two configs together. The receiver's values are overwritten with the patch's 165 // values except when the patch's values are nil. 166 func Merge(cfg *model.Config, patch *model.Config, mergeConfig *utils.MergeConfig) (*model.Config, error) { 167 ret, err := utils.Merge(cfg, patch, mergeConfig) 168 if err != nil { 169 return nil, err 170 } 171 172 retCfg := ret.(model.Config) 173 return &retCfg, nil 174 } 175 176 func IsDatabaseDSN(dsn string) bool { 177 return strings.HasPrefix(dsn, "mysql://") || strings.HasPrefix(dsn, "postgres://") 178 } 179 180 // stripPassword remove the password from a given DSN 181 func stripPassword(dsn, schema string) string { 182 prefix := schema + "://" 183 dsn = strings.TrimPrefix(dsn, prefix) 184 185 i := strings.Index(dsn, ":") 186 j := strings.LastIndex(dsn, "@") 187 188 // Return error if no @ sign is found 189 if j < 0 { 190 return "(omitted due to error parsing the DSN)" 191 } 192 193 // Return back the input if no password is found 194 if i < 0 || i > j { 195 return prefix + dsn 196 } 197 198 return prefix + dsn[:i+1] + dsn[j:] 199 } 200 201 func IsJsonMap(data string) bool { 202 var m map[string]interface{} 203 return json.Unmarshal([]byte(data), &m) == nil 204 } 205 206 func JSONToLogTargetCfg(data []byte) (mlog.LogTargetCfg, error) { 207 cfg := make(mlog.LogTargetCfg) 208 err := json.Unmarshal(data, &cfg) 209 if err != nil { 210 return nil, err 211 } 212 return cfg, nil 213 } 214 215 func GetValueByPath(path []string, obj interface{}) (interface{}, bool) { 216 r := reflect.ValueOf(obj) 217 var val reflect.Value 218 if r.Kind() == reflect.Map { 219 val = r.MapIndex(reflect.ValueOf(path[0])) 220 if val.IsValid() { 221 val = val.Elem() 222 } 223 } else { 224 val = r.FieldByName(path[0]) 225 } 226 227 if !val.IsValid() { 228 return nil, false 229 } 230 231 switch { 232 case len(path) == 1: 233 return val.Interface(), true 234 case val.Kind() == reflect.Struct: 235 return GetValueByPath(path[1:], val.Interface()) 236 case val.Kind() == reflect.Map: 237 remainingPath := strings.Join(path[1:], ".") 238 mapIter := val.MapRange() 239 for mapIter.Next() { 240 key := mapIter.Key().String() 241 if strings.HasPrefix(remainingPath, key) { 242 i := strings.Count(key, ".") + 2 // number of dots + a dot on each side 243 mapVal := mapIter.Value() 244 // if no sub field path specified, return the object 245 if len(path[i:]) == 0 { 246 return mapVal.Interface(), true 247 } 248 data := mapVal.Interface() 249 if mapVal.Kind() == reflect.Ptr { 250 data = mapVal.Elem().Interface() // if value is a pointer, dereference it 251 } 252 // pass subpath 253 return GetValueByPath(path[i:], data) 254 } 255 } 256 } 257 return nil, false 258 }