2021-12-31 08:22:10 +00:00
|
|
|
package env
|
|
|
|
|
|
|
|
import (
|
2022-07-10 16:19:33 +00:00
|
|
|
"context"
|
2021-12-31 08:22:10 +00:00
|
|
|
"encoding/json"
|
|
|
|
"os"
|
2022-05-31 07:41:54 +00:00
|
|
|
"reflect"
|
2022-01-20 11:22:37 +00:00
|
|
|
"strconv"
|
2021-12-31 08:22:10 +00:00
|
|
|
"strings"
|
|
|
|
|
2022-02-26 15:06:22 +00:00
|
|
|
"github.com/google/uuid"
|
2022-05-13 01:58:31 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2022-02-26 15:06:22 +00:00
|
|
|
|
2021-12-31 08:22:10 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/constants"
|
2022-02-26 15:06:22 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/crypto"
|
2021-12-31 08:22:10 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/db"
|
2022-01-21 08:04:04 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/db/models"
|
2022-05-29 11:52:46 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
2022-05-31 07:41:54 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/utils"
|
2021-12-31 08:22:10 +00:00
|
|
|
)
|
|
|
|
|
2022-05-31 07:41:54 +00:00
|
|
|
func fixBackwardCompatibility(data map[string]interface{}) (bool, map[string]interface{}) {
|
|
|
|
result := data
|
|
|
|
// check if env data is stored in older format
|
|
|
|
hasOlderFormat := false
|
|
|
|
if _, ok := result["bool_env"]; ok {
|
|
|
|
for key, value := range result["bool_env"].(map[string]interface{}) {
|
|
|
|
result[key] = value
|
|
|
|
}
|
|
|
|
hasOlderFormat = true
|
|
|
|
delete(result, "bool_env")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := result["string_env"]; ok {
|
|
|
|
for key, value := range result["string_env"].(map[string]interface{}) {
|
|
|
|
result[key] = value
|
|
|
|
}
|
|
|
|
hasOlderFormat = true
|
|
|
|
delete(result, "string_env")
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := result["slice_env"]; ok {
|
|
|
|
for key, value := range result["slice_env"].(map[string]interface{}) {
|
|
|
|
typeOfValue := reflect.TypeOf(value)
|
|
|
|
if strings.Contains(typeOfValue.String(), "[]string") {
|
|
|
|
result[key] = strings.Join(value.([]string), ",")
|
|
|
|
}
|
|
|
|
if strings.Contains(typeOfValue.String(), "[]interface") {
|
|
|
|
result[key] = strings.Join(utils.ConvertInterfaceToStringSlice(value), ",")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hasOlderFormat = true
|
|
|
|
delete(result, "slice_env")
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasOlderFormat, result
|
|
|
|
}
|
|
|
|
|
2022-02-26 04:14:55 +00:00
|
|
|
// GetEnvData returns the env data from database
|
2022-05-29 11:52:46 +00:00
|
|
|
func GetEnvData() (map[string]interface{}, error) {
|
|
|
|
var result map[string]interface{}
|
2022-07-10 16:19:33 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
env, err := db.Provider.GetEnv(ctx)
|
2022-02-26 04:14:55 +00:00
|
|
|
// config not found in db
|
2023-08-01 10:39:17 +00:00
|
|
|
if err != nil || env == nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while getting env data from db: ", err)
|
2022-02-26 04:14:55 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
encryptionKey := env.Hash
|
2022-02-28 15:56:49 +00:00
|
|
|
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
2022-02-26 04:14:55 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting encryption key: ", err)
|
2022-02-26 04:14:55 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2022-05-29 11:52:46 +00:00
|
|
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
2022-03-07 10:05:33 +00:00
|
|
|
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting env data from B64: ", err)
|
2022-03-07 10:05:33 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
2022-02-26 04:14:55 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting env data from AES: ", err)
|
2022-02-26 04:14:55 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2022-03-07 09:59:37 +00:00
|
|
|
err = json.Unmarshal(decryptedConfigs, &result)
|
2022-02-26 04:14:55 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while unmarshalling env data: ", err)
|
2022-02-26 04:14:55 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2022-05-31 07:41:54 +00:00
|
|
|
hasOlderFormat, result := fixBackwardCompatibility(result)
|
2022-05-31 02:44:03 +00:00
|
|
|
|
|
|
|
if hasOlderFormat {
|
2022-05-31 07:41:54 +00:00
|
|
|
err = memorystore.Provider.UpdateEnvStore(result)
|
2022-05-31 02:44:03 +00:00
|
|
|
if err != nil {
|
2022-05-31 07:41:54 +00:00
|
|
|
log.Debug("Error while updating env store: ", err)
|
2022-05-31 02:44:03 +00:00
|
|
|
return result, err
|
|
|
|
}
|
2022-05-31 07:41:54 +00:00
|
|
|
|
2022-05-31 02:44:03 +00:00
|
|
|
}
|
|
|
|
|
2022-02-26 04:14:55 +00:00
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2022-01-17 06:02:13 +00:00
|
|
|
// PersistEnv persists the environment variables to the database
|
2021-12-31 08:22:10 +00:00
|
|
|
func PersistEnv() error {
|
2022-07-10 16:19:33 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
env, err := db.Provider.GetEnv(ctx)
|
2021-12-31 08:22:10 +00:00
|
|
|
// config not found in db
|
2023-08-01 10:39:17 +00:00
|
|
|
if err != nil || env == nil {
|
2021-12-31 08:22:10 +00:00
|
|
|
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
|
|
|
|
hash := uuid.New().String()[:36-4]
|
2022-05-31 07:41:54 +00:00
|
|
|
err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, hash)
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Error while updating encryption env variable: ", err)
|
|
|
|
return err
|
|
|
|
}
|
2022-02-28 15:56:49 +00:00
|
|
|
encodedHash := crypto.EncryptB64(hash)
|
2022-05-29 11:52:46 +00:00
|
|
|
res, err := memorystore.Provider.GetEnvStore()
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Error while getting env store: ", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
encryptedConfig, err := crypto.EncryptEnvData(res)
|
2021-12-31 08:22:10 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while encrypting env data: ", err)
|
2021-12-31 08:22:10 +00:00
|
|
|
return err
|
|
|
|
}
|
2023-07-31 11:12:11 +00:00
|
|
|
env = &models.Env{
|
2022-01-20 11:22:37 +00:00
|
|
|
Hash: encodedHash,
|
|
|
|
EnvData: encryptedConfig,
|
2021-12-31 08:22:10 +00:00
|
|
|
}
|
2023-07-31 11:12:11 +00:00
|
|
|
_, err = db.Provider.AddEnv(ctx, env)
|
2022-02-26 04:14:55 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while persisting env data to db: ", err)
|
2022-02-26 04:14:55 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
} else {
|
|
|
|
// decrypt the config data from db
|
|
|
|
// decryption can be done using the hash stored in db
|
2022-01-20 11:22:37 +00:00
|
|
|
encryptionKey := env.Hash
|
2022-02-28 15:56:49 +00:00
|
|
|
decryptedEncryptionKey, err := crypto.DecryptB64(encryptionKey)
|
2021-12-31 08:22:10 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting encryption key: ", err)
|
2021-12-31 08:22:10 +00:00
|
|
|
return err
|
|
|
|
}
|
2022-01-17 06:02:13 +00:00
|
|
|
|
2022-05-29 11:52:46 +00:00
|
|
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
2022-01-29 11:32:44 +00:00
|
|
|
|
2022-03-07 10:03:39 +00:00
|
|
|
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting env data from B64: ", err)
|
2022-03-07 10:03:39 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
2021-12-31 08:22:10 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while decrypting env data from AES: ", err)
|
2021-12-31 08:22:10 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-01-20 11:22:37 +00:00
|
|
|
// temp store variable
|
2022-05-29 11:52:46 +00:00
|
|
|
storeData := map[string]interface{}{}
|
2021-12-31 08:22:10 +00:00
|
|
|
|
2022-03-07 09:59:37 +00:00
|
|
|
err = json.Unmarshal(decryptedConfigs, &storeData)
|
2021-12-31 08:22:10 +00:00
|
|
|
if err != nil {
|
2022-07-12 06:18:42 +00:00
|
|
|
log.Debug("Error while un-marshalling env data: ", err)
|
2021-12-31 08:22:10 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-31 07:41:54 +00:00
|
|
|
hasOlderFormat, result := fixBackwardCompatibility(storeData)
|
|
|
|
if hasOlderFormat {
|
|
|
|
err = memorystore.Provider.UpdateEnvStore(result)
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Error while updating env store: ", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-12-31 08:22:10 +00:00
|
|
|
// if env is changed via env file or OS env
|
|
|
|
// give that higher preference and update db, but we don't recommend it
|
|
|
|
|
|
|
|
hasChanged := false
|
2022-05-29 11:52:46 +00:00
|
|
|
for key, value := range storeData {
|
2022-02-26 15:06:22 +00:00
|
|
|
// don't override unexposed envs
|
2022-05-29 11:52:46 +00:00
|
|
|
// check only for derivative keys
|
|
|
|
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
|
|
|
// as we have removed it from json
|
2022-03-07 13:19:18 +00:00
|
|
|
if key != constants.EnvKeyEncryptionKey {
|
2022-01-20 11:22:37 +00:00
|
|
|
envValue := strings.TrimSpace(os.Getenv(key))
|
|
|
|
if envValue != "" {
|
2022-05-29 11:52:46 +00:00
|
|
|
switch key {
|
2023-11-16 13:00:54 +00:00
|
|
|
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround, constants.EnvKeyDisableTOTPLogin, constants.EnvKeyDisableMailOTPLogin:
|
2022-05-29 11:52:46 +00:00
|
|
|
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
|
|
|
|
if value.(bool) != envValueBool {
|
|
|
|
storeData[key] = envValueBool
|
|
|
|
hasChanged = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
2022-05-31 02:44:03 +00:00
|
|
|
if value != nil && value.(string) != envValue {
|
2022-05-29 11:52:46 +00:00
|
|
|
storeData[key] = envValue
|
|
|
|
hasChanged = true
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-20 11:22:37 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
|
|
|
|
// handle derivative cases like disabling email verification & magic login
|
|
|
|
// in case SMTP is off but env is set to true
|
2022-05-29 11:52:46 +00:00
|
|
|
if storeData[constants.EnvKeySmtpHost] == "" || storeData[constants.EnvKeySmtpUsername] == "" || storeData[constants.EnvKeySmtpPassword] == "" || storeData[constants.EnvKeySenderEmail] == "" && storeData[constants.EnvKeySmtpPort] == "" {
|
2022-07-29 14:19:50 +00:00
|
|
|
storeData[constants.EnvKeyIsEmailServiceEnabled] = false
|
|
|
|
|
2023-11-25 06:04:01 +00:00
|
|
|
if val, ok := storeData[constants.EnvKeyDisableEmailVerification]; ok && val != nil && !val.(bool) {
|
2022-05-29 11:52:46 +00:00
|
|
|
storeData[constants.EnvKeyDisableEmailVerification] = true
|
2021-12-31 08:22:10 +00:00
|
|
|
hasChanged = true
|
|
|
|
}
|
|
|
|
|
2023-11-25 06:04:01 +00:00
|
|
|
if val, ok := storeData[constants.EnvKeyDisableMagicLinkLogin]; ok && val != nil && !val.(bool) {
|
2022-05-29 11:52:46 +00:00
|
|
|
storeData[constants.EnvKeyDisableMagicLinkLogin] = true
|
2021-12-31 08:22:10 +00:00
|
|
|
hasChanged = true
|
|
|
|
}
|
2023-11-16 13:00:54 +00:00
|
|
|
|
2023-11-25 06:04:01 +00:00
|
|
|
if val, ok := storeData[constants.EnvKeyDisableMailOTPLogin]; ok && val != nil && !val.(bool) {
|
2023-11-16 13:00:54 +00:00
|
|
|
storeData[constants.EnvKeyDisableMailOTPLogin] = true
|
|
|
|
hasChanged = true
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
}
|
2022-03-25 14:59:00 +00:00
|
|
|
|
2022-05-29 11:52:46 +00:00
|
|
|
err = memorystore.Provider.UpdateEnvStore(storeData)
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Error while updating env store: ", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-26 15:06:22 +00:00
|
|
|
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while generating JWK: ", err)
|
2022-02-26 15:06:22 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
// updating jwk
|
2022-05-29 11:52:46 +00:00
|
|
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJWK, jwk)
|
2022-02-26 04:14:55 +00:00
|
|
|
|
2021-12-31 08:22:10 +00:00
|
|
|
if hasChanged {
|
2022-02-28 15:56:49 +00:00
|
|
|
encryptedConfig, err := crypto.EncryptEnvData(storeData)
|
2021-12-31 11:33:37 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Error while encrypting env data: ", err)
|
2021-12-31 11:33:37 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
|
2022-01-20 11:22:37 +00:00
|
|
|
env.EnvData = encryptedConfig
|
2022-07-10 16:19:33 +00:00
|
|
|
_, err = db.Provider.UpdateEnv(ctx, env)
|
2021-12-31 11:33:37 +00:00
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Failed to Update Config: ", err)
|
2021-12-31 11:33:37 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-12-31 08:22:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|