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  }