github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/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/mattermost/mattermost-server/v5/mlog"
    12  	"github.com/mattermost/mattermost-server/v5/model"
    13  	"github.com/mattermost/mattermost-server/v5/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.
    90  func fixConfig(cfg *model.Config) {
    91  	// Ensure SiteURL has no trailing slash.
    92  	if strings.HasSuffix(*cfg.ServiceSettings.SiteURL, "/") {
    93  		*cfg.ServiceSettings.SiteURL = strings.TrimRight(*cfg.ServiceSettings.SiteURL, "/")
    94  	}
    95  
    96  	// Ensure the directory for a local file store has a trailing slash.
    97  	if *cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
    98  		if *cfg.FileSettings.Directory != "" && !strings.HasSuffix(*cfg.FileSettings.Directory, "/") {
    99  			*cfg.FileSettings.Directory += "/"
   100  		}
   101  	}
   102  
   103  	FixInvalidLocales(cfg)
   104  }
   105  
   106  // FixInvalidLocales checks and corrects the given config for invalid locale-related settings.
   107  //
   108  // Ideally, this function would be completely internal, but it's currently exposed to allow the cli
   109  // to test the config change before allowing the save.
   110  func FixInvalidLocales(cfg *model.Config) bool {
   111  	var changed bool
   112  
   113  	locales := utils.GetSupportedLocales()
   114  	if _, ok := locales[*cfg.LocalizationSettings.DefaultServerLocale]; !ok {
   115  		*cfg.LocalizationSettings.DefaultServerLocale = model.DEFAULT_LOCALE
   116  		mlog.Warn("DefaultServerLocale must be one of the supported locales. Setting DefaultServerLocale to en as default value.")
   117  		changed = true
   118  	}
   119  
   120  	if _, ok := locales[*cfg.LocalizationSettings.DefaultClientLocale]; !ok {
   121  		*cfg.LocalizationSettings.DefaultClientLocale = model.DEFAULT_LOCALE
   122  		mlog.Warn("DefaultClientLocale must be one of the supported locales. Setting DefaultClientLocale to en as default value.")
   123  		changed = true
   124  	}
   125  
   126  	if *cfg.LocalizationSettings.AvailableLocales != "" {
   127  		isDefaultClientLocaleInAvailableLocales := false
   128  		for _, word := range strings.Split(*cfg.LocalizationSettings.AvailableLocales, ",") {
   129  			if _, ok := locales[word]; !ok {
   130  				*cfg.LocalizationSettings.AvailableLocales = ""
   131  				isDefaultClientLocaleInAvailableLocales = true
   132  				mlog.Warn("AvailableLocales must include DefaultClientLocale. Setting AvailableLocales to all locales as default value.")
   133  				changed = true
   134  				break
   135  			}
   136  
   137  			if word == *cfg.LocalizationSettings.DefaultClientLocale {
   138  				isDefaultClientLocaleInAvailableLocales = true
   139  			}
   140  		}
   141  
   142  		availableLocales := *cfg.LocalizationSettings.AvailableLocales
   143  
   144  		if !isDefaultClientLocaleInAvailableLocales {
   145  			availableLocales += "," + *cfg.LocalizationSettings.DefaultClientLocale
   146  			mlog.Warn("Adding DefaultClientLocale to AvailableLocales.")
   147  			changed = true
   148  		}
   149  
   150  		*cfg.LocalizationSettings.AvailableLocales = strings.Join(utils.RemoveDuplicatesFromStringArray(strings.Split(availableLocales, ",")), ",")
   151  	}
   152  
   153  	return changed
   154  }
   155  
   156  // Merge merges two configs together. The receiver's values are overwritten with the patch's
   157  // values except when the patch's values are nil.
   158  func Merge(cfg *model.Config, patch *model.Config, mergeConfig *utils.MergeConfig) (*model.Config, error) {
   159  	ret, err := utils.Merge(cfg, patch, mergeConfig)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	retCfg := ret.(model.Config)
   165  	return &retCfg, nil
   166  }
   167  
   168  func IsDatabaseDSN(dsn string) bool {
   169  	return strings.HasPrefix(dsn, "mysql://") || strings.HasPrefix(dsn, "postgres://")
   170  }
   171  
   172  // stripPassword remove the password from a given DSN
   173  func stripPassword(dsn, schema string) string {
   174  	prefix := schema + "://"
   175  	dsn = strings.TrimPrefix(dsn, prefix)
   176  
   177  	i := strings.Index(dsn, ":")
   178  	j := strings.LastIndex(dsn, "@")
   179  
   180  	// Return error if no @ sign is found
   181  	if j < 0 {
   182  		return "(omitted due to error parsing the DSN)"
   183  	}
   184  
   185  	// Return back the input if no password is found
   186  	if i < 0 || i > j {
   187  		return prefix + dsn
   188  	}
   189  
   190  	return prefix + dsn[:i+1] + dsn[j:]
   191  }
   192  
   193  func IsJsonMap(data string) bool {
   194  	var m map[string]interface{}
   195  	return json.Unmarshal([]byte(data), &m) == nil
   196  }
   197  
   198  func JSONToLogTargetCfg(data []byte) (mlog.LogTargetCfg, error) {
   199  	cfg := make(mlog.LogTargetCfg)
   200  	err := json.Unmarshal(data, &cfg)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	return cfg, nil
   205  }
   206  
   207  func GetValueByPath(path []string, obj interface{}) (interface{}, bool) {
   208  	r := reflect.ValueOf(obj)
   209  	var val reflect.Value
   210  	if r.Kind() == reflect.Map {
   211  		val = r.MapIndex(reflect.ValueOf(path[0]))
   212  		if val.IsValid() {
   213  			val = val.Elem()
   214  		}
   215  	} else {
   216  		val = r.FieldByName(path[0])
   217  	}
   218  
   219  	if !val.IsValid() {
   220  		return nil, false
   221  	}
   222  
   223  	switch {
   224  	case len(path) == 1:
   225  		return val.Interface(), true
   226  	case val.Kind() == reflect.Struct:
   227  		return GetValueByPath(path[1:], val.Interface())
   228  	case val.Kind() == reflect.Map:
   229  		remainingPath := strings.Join(path[1:], ".")
   230  		mapIter := val.MapRange()
   231  		for mapIter.Next() {
   232  			key := mapIter.Key().String()
   233  			if strings.HasPrefix(remainingPath, key) {
   234  				i := strings.Count(key, ".") + 2 // number of dots + a dot on each side
   235  				mapVal := mapIter.Value()
   236  				// if no sub field path specified, return the object
   237  				if len(path[i:]) == 0 {
   238  					return mapVal.Interface(), true
   239  				}
   240  				data := mapVal.Interface()
   241  				if mapVal.Kind() == reflect.Ptr {
   242  					data = mapVal.Elem().Interface() // if value is a pointer, dereference it
   243  				}
   244  				// pass subpath
   245  				return GetValueByPath(path[i:], data)
   246  			}
   247  		}
   248  	}
   249  	return nil, false
   250  }