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  }