pkg/settings/settings.go (111 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package settings
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"github.com/Azure/azure-extension-platform/pkg/decrypt"
"github.com/Azure/azure-extension-platform/pkg/extensionerrors"
"github.com/Azure/azure-extension-platform/pkg/handlerenv"
"github.com/Azure/azure-extension-platform/pkg/logging"
)
const (
settingsFileSuffix = ".settings"
disableFileName = "disabled"
)
// HandlerSettings contains the decrypted settings for the extension
type HandlerSettings struct {
// string containing the json sent for the public settings
PublicSettings string
// string containing the decrypted protected settings
ProtectedSettings string
}
// handlerSettings is an internal structure used to deserialize the file
type handlerSettings struct {
PublicSettings interface{} `json:"publicSettings"`
ProtectedSettingsBase64 string `json:"protectedSettings"`
SettingsCertThumbprint string `json:"protectedSettingsCertThumbprint"`
}
type handlerSettingsFile struct {
RuntimeSettings []handlerSettingsContainer `json:"runtimeSettings"`
}
type handlerSettingsContainer struct {
HandlerSettings handlerSettings `json:"handlerSettings"`
}
// GetHandlerSettings reads and parses the handler's settings in an OS independent manner
func GetHandlerSettings(el logging.ILogger, he *handlerenv.HandlerEnvironment, seqNo uint) (hs *HandlerSettings, _ error) {
// The file will be under the config folder with the path {seqNo}.settings
settingsFileName := filepath.Join(he.ConfigFolder, fmt.Sprintf("%d%s", seqNo, settingsFileSuffix))
parsedHs, err := parseHandlerSettingsFile(el, settingsFileName)
if err != nil {
return hs, err
}
protectedSettings, err := unmarshalProtectedSettings(el, he.ConfigFolder, parsedHs)
if err != nil {
return hs, err
}
var publicSettingJsonString = ""
// parsedHS.PublicSettings is an interface, has to be marshaled to get the string representation
if parsedHs.PublicSettings != nil {
jsonBytes, err := json.Marshal(parsedHs.PublicSettings)
if err != nil {
return hs, err
}
publicSettingJsonString = string(jsonBytes)
}
hs = &HandlerSettings{
PublicSettings: publicSettingJsonString,
ProtectedSettings: protectedSettings,
}
return hs, nil
}
// unmarshalProtectedSettings decodes the protected settings from handler
// runtime settings JSON file, decrypts it using the certificates and unmarshals
// into the given struct v.
func unmarshalProtectedSettings(el logging.ILogger, configFolder string, hs handlerSettings) (string, error) {
if hs.ProtectedSettingsBase64 == "" {
// No protected settings
return "", nil
}
if hs.SettingsCertThumbprint == "" {
el.Error("parseHandlerSettingsFile failed due to no settings cert thumbprint")
return "", extensionerrors.ErrNoCertificateThumbprint
}
decoded, err := base64.StdEncoding.DecodeString(hs.ProtectedSettingsBase64)
if err != nil {
el.Error("parseHandlerSettingsFile failed to decode base64: %v", err)
return "", extensionerrors.ErrInvalidProtectedSettingsData
}
v, err := decrypt.DecryptProtectedSettings(configFolder, hs.SettingsCertThumbprint, decoded)
return v, err
}
// parseHandlerSettings parses a handler settings file (e.g. 0.settings) and
// returns it as a structured object.
func parseHandlerSettingsFile(el logging.ILogger, path string) (h handlerSettings, _ error) {
b, err := ioutil.ReadFile(path)
if err != nil {
el.Error("parseHandlerSettingsFile failed. Error reading %s: %v", path, err)
return h, extensionerrors.ErrInvalidSettingsFile
}
if len(b) == 0 { // if no config is specified, we get an empty file
return h, nil
}
var f handlerSettingsFile
if err := json.Unmarshal(b, &f); err != nil {
el.Error("parseHandlerSettingsFile failed. error parsing json: %v", err)
return h, extensionerrors.ErrInvalidSettingsFile
}
if len(f.RuntimeSettings) != 1 {
el.Error("parseHandlerSettingsFile failed. wrong runtimeSettings count. expected:1, got:%d", len(f.RuntimeSettings))
return h, extensionerrors.ErrInvalidSettingsRuntimeSettingsCount
}
return f.RuntimeSettings[0].HandlerSettings, nil
}
// CleanUpSettings replaces the protected settings for all settings files [ex: 0.settings, etc] to ensure no
// protected settings are logged in VM
func CleanUpSettings(el logging.ILogger, configFolder string) {
configDir, err := ioutil.ReadDir(configFolder)
if err != nil {
el.Error("error clearing config file: %v", err)
return
}
content := []byte("")
for _, file := range configDir {
if strings.Compare(filepath.Ext(file.Name()), settingsFileSuffix) == 0 { //checking if its a settings file
filePath := filepath.Join(configFolder, file.Name())
err = ioutil.WriteFile(filePath, content, 0644)
if err != nil {
el.Error("error clearing %s, err %v", file.Name(), err)
} else {
el.Info("%s cleared successfully", file.Name())
}
}
}
}