feat: persist encrypted env
This commit is contained in:
227
server/env/env.go
vendored
227
server/env/env.go
vendored
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
@@ -20,163 +21,169 @@ var (
|
||||
|
||||
// InitEnv -> to initialize env and through error if required env are not present
|
||||
func InitEnv() {
|
||||
if constants.ENV_PATH == "" {
|
||||
constants.ENV_PATH = `.env`
|
||||
if constants.EnvData.ENV_PATH == "" {
|
||||
constants.EnvData.ENV_PATH = `.env`
|
||||
}
|
||||
|
||||
if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" {
|
||||
constants.ENV_PATH = *ARG_ENV_FILE
|
||||
constants.EnvData.ENV_PATH = *ARG_ENV_FILE
|
||||
}
|
||||
|
||||
err := godotenv.Load(constants.ENV_PATH)
|
||||
err := godotenv.Load(constants.EnvData.ENV_PATH)
|
||||
if err != nil {
|
||||
log.Printf("error loading %s file", constants.ENV_PATH)
|
||||
log.Printf("error loading %s file", constants.EnvData.ENV_PATH)
|
||||
}
|
||||
|
||||
if constants.ADMIN_SECRET == "" {
|
||||
constants.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
|
||||
if constants.ADMIN_SECRET == "" {
|
||||
panic("root admin secret is required")
|
||||
}
|
||||
if constants.EnvData.ADMIN_SECRET == "" {
|
||||
constants.EnvData.ADMIN_SECRET = os.Getenv("ADMIN_SECRET")
|
||||
}
|
||||
|
||||
if constants.ENV == "" {
|
||||
constants.ENV = os.Getenv("ENV")
|
||||
if constants.ENV == "" {
|
||||
constants.ENV = "production"
|
||||
}
|
||||
|
||||
if constants.ENV == "production" {
|
||||
constants.IS_PROD = true
|
||||
os.Setenv("GIN_MODE", "release")
|
||||
} else {
|
||||
constants.IS_PROD = false
|
||||
}
|
||||
}
|
||||
|
||||
if constants.DATABASE_TYPE == "" {
|
||||
constants.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
|
||||
log.Println(constants.DATABASE_TYPE)
|
||||
if constants.EnvData.DATABASE_TYPE == "" {
|
||||
constants.EnvData.DATABASE_TYPE = os.Getenv("DATABASE_TYPE")
|
||||
log.Println(constants.EnvData.DATABASE_TYPE)
|
||||
|
||||
if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" {
|
||||
constants.DATABASE_TYPE = *ARG_DB_TYPE
|
||||
constants.EnvData.DATABASE_TYPE = *ARG_DB_TYPE
|
||||
}
|
||||
|
||||
if constants.DATABASE_TYPE == "" {
|
||||
if constants.EnvData.DATABASE_TYPE == "" {
|
||||
panic("DATABASE_TYPE is required")
|
||||
}
|
||||
}
|
||||
|
||||
if constants.DATABASE_URL == "" {
|
||||
constants.DATABASE_URL = os.Getenv("DATABASE_URL")
|
||||
if constants.EnvData.DATABASE_URL == "" {
|
||||
constants.EnvData.DATABASE_URL = os.Getenv("DATABASE_URL")
|
||||
|
||||
if ARG_DB_URL != nil && *ARG_DB_URL != "" {
|
||||
constants.DATABASE_URL = *ARG_DB_URL
|
||||
constants.EnvData.DATABASE_URL = *ARG_DB_URL
|
||||
}
|
||||
|
||||
if constants.DATABASE_URL == "" {
|
||||
if constants.EnvData.DATABASE_URL == "" {
|
||||
panic("DATABASE_URL is required")
|
||||
}
|
||||
}
|
||||
|
||||
if constants.DATABASE_NAME == "" {
|
||||
constants.DATABASE_NAME = os.Getenv("DATABASE_NAME")
|
||||
if constants.DATABASE_NAME == "" {
|
||||
constants.DATABASE_NAME = "authorizer"
|
||||
if constants.EnvData.DATABASE_NAME == "" {
|
||||
constants.EnvData.DATABASE_NAME = os.Getenv("DATABASE_NAME")
|
||||
if constants.EnvData.DATABASE_NAME == "" {
|
||||
constants.EnvData.DATABASE_NAME = "authorizer"
|
||||
}
|
||||
}
|
||||
|
||||
if constants.SMTP_HOST == "" {
|
||||
constants.SMTP_HOST = os.Getenv("SMTP_HOST")
|
||||
}
|
||||
if constants.EnvData.ENV == "" {
|
||||
constants.EnvData.ENV = os.Getenv("ENV")
|
||||
if constants.EnvData.ENV == "" {
|
||||
constants.EnvData.ENV = "production"
|
||||
}
|
||||
|
||||
if constants.SMTP_PORT == "" {
|
||||
constants.SMTP_PORT = os.Getenv("SMTP_PORT")
|
||||
}
|
||||
|
||||
if constants.SENDER_EMAIL == "" {
|
||||
constants.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
|
||||
}
|
||||
|
||||
if constants.SENDER_PASSWORD == "" {
|
||||
constants.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
|
||||
}
|
||||
|
||||
if constants.JWT_SECRET == "" {
|
||||
constants.JWT_SECRET = os.Getenv("JWT_SECRET")
|
||||
}
|
||||
|
||||
if constants.JWT_TYPE == "" {
|
||||
constants.JWT_TYPE = os.Getenv("JWT_TYPE")
|
||||
}
|
||||
|
||||
if constants.JWT_ROLE_CLAIM == "" {
|
||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||
|
||||
if constants.JWT_ROLE_CLAIM == "" {
|
||||
constants.JWT_ROLE_CLAIM = "role"
|
||||
if constants.EnvData.ENV == "production" {
|
||||
constants.EnvData.IS_PROD = true
|
||||
os.Setenv("GIN_MODE", "release")
|
||||
} else {
|
||||
constants.EnvData.IS_PROD = false
|
||||
}
|
||||
}
|
||||
|
||||
if constants.AUTHORIZER_URL == "" {
|
||||
constants.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
|
||||
if constants.EnvData.SMTP_HOST == "" {
|
||||
constants.EnvData.SMTP_HOST = os.Getenv("SMTP_HOST")
|
||||
}
|
||||
|
||||
if constants.EnvData.SMTP_PORT == "" {
|
||||
constants.EnvData.SMTP_PORT = os.Getenv("SMTP_PORT")
|
||||
}
|
||||
|
||||
if constants.EnvData.SENDER_EMAIL == "" {
|
||||
constants.EnvData.SENDER_EMAIL = os.Getenv("SENDER_EMAIL")
|
||||
}
|
||||
|
||||
if constants.EnvData.SENDER_PASSWORD == "" {
|
||||
constants.EnvData.SENDER_PASSWORD = os.Getenv("SENDER_PASSWORD")
|
||||
}
|
||||
|
||||
if constants.EnvData.JWT_SECRET == "" {
|
||||
constants.EnvData.JWT_SECRET = os.Getenv("JWT_SECRET")
|
||||
if constants.EnvData.JWT_SECRET == "" {
|
||||
constants.EnvData.JWT_SECRET = uuid.New().String()
|
||||
}
|
||||
}
|
||||
|
||||
if constants.EnvData.JWT_TYPE == "" {
|
||||
constants.EnvData.JWT_TYPE = os.Getenv("JWT_TYPE")
|
||||
if constants.EnvData.JWT_TYPE == "" {
|
||||
constants.EnvData.JWT_TYPE = "HS256"
|
||||
}
|
||||
}
|
||||
|
||||
if constants.EnvData.JWT_ROLE_CLAIM == "" {
|
||||
constants.EnvData.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||
|
||||
if constants.EnvData.JWT_ROLE_CLAIM == "" {
|
||||
constants.EnvData.JWT_ROLE_CLAIM = "role"
|
||||
}
|
||||
}
|
||||
|
||||
if constants.EnvData.AUTHORIZER_URL == "" {
|
||||
constants.EnvData.AUTHORIZER_URL = strings.TrimSuffix(os.Getenv("AUTHORIZER_URL"), "/")
|
||||
|
||||
if ARG_AUTHORIZER_URL != nil && *ARG_AUTHORIZER_URL != "" {
|
||||
constants.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
|
||||
constants.EnvData.AUTHORIZER_URL = *ARG_AUTHORIZER_URL
|
||||
}
|
||||
}
|
||||
|
||||
if constants.PORT == "" {
|
||||
constants.PORT = os.Getenv("PORT")
|
||||
if constants.PORT == "" {
|
||||
constants.PORT = "8080"
|
||||
if constants.EnvData.PORT == "" {
|
||||
constants.EnvData.PORT = os.Getenv("PORT")
|
||||
if constants.EnvData.PORT == "" {
|
||||
constants.EnvData.PORT = "8080"
|
||||
}
|
||||
}
|
||||
|
||||
if constants.REDIS_URL == "" {
|
||||
constants.REDIS_URL = os.Getenv("REDIS_URL")
|
||||
if constants.EnvData.REDIS_URL == "" {
|
||||
constants.EnvData.REDIS_URL = os.Getenv("REDIS_URL")
|
||||
}
|
||||
|
||||
if constants.COOKIE_NAME == "" {
|
||||
constants.COOKIE_NAME = os.Getenv("COOKIE_NAME")
|
||||
if constants.EnvData.COOKIE_NAME == "" {
|
||||
constants.EnvData.COOKIE_NAME = os.Getenv("COOKIE_NAME")
|
||||
if constants.EnvData.COOKIE_NAME == "" {
|
||||
constants.EnvData.COOKIE_NAME = "authorizer"
|
||||
}
|
||||
}
|
||||
|
||||
if constants.GOOGLE_CLIENT_ID == "" {
|
||||
constants.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
|
||||
if constants.EnvData.GOOGLE_CLIENT_ID == "" {
|
||||
constants.EnvData.GOOGLE_CLIENT_ID = os.Getenv("GOOGLE_CLIENT_ID")
|
||||
}
|
||||
|
||||
if constants.GOOGLE_CLIENT_SECRET == "" {
|
||||
constants.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
|
||||
if constants.EnvData.GOOGLE_CLIENT_SECRET == "" {
|
||||
constants.EnvData.GOOGLE_CLIENT_SECRET = os.Getenv("GOOGLE_CLIENT_SECRET")
|
||||
}
|
||||
|
||||
if constants.GITHUB_CLIENT_ID == "" {
|
||||
constants.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
|
||||
if constants.EnvData.GITHUB_CLIENT_ID == "" {
|
||||
constants.EnvData.GITHUB_CLIENT_ID = os.Getenv("GITHUB_CLIENT_ID")
|
||||
}
|
||||
|
||||
if constants.GITHUB_CLIENT_SECRET == "" {
|
||||
constants.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
|
||||
if constants.EnvData.GITHUB_CLIENT_SECRET == "" {
|
||||
constants.EnvData.GITHUB_CLIENT_SECRET = os.Getenv("GITHUB_CLIENT_SECRET")
|
||||
}
|
||||
|
||||
if constants.FACEBOOK_CLIENT_ID == "" {
|
||||
constants.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
|
||||
if constants.EnvData.FACEBOOK_CLIENT_ID == "" {
|
||||
constants.EnvData.FACEBOOK_CLIENT_ID = os.Getenv("FACEBOOK_CLIENT_ID")
|
||||
}
|
||||
|
||||
if constants.FACEBOOK_CLIENT_SECRET == "" {
|
||||
constants.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
|
||||
if constants.EnvData.FACEBOOK_CLIENT_SECRET == "" {
|
||||
constants.EnvData.FACEBOOK_CLIENT_SECRET = os.Getenv("FACEBOOK_CLIENT_SECRET")
|
||||
}
|
||||
|
||||
if constants.RESET_PASSWORD_URL == "" {
|
||||
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||
if constants.EnvData.RESET_PASSWORD_URL == "" {
|
||||
constants.EnvData.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||
}
|
||||
|
||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
|
||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
|
||||
constants.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
|
||||
constants.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
|
||||
constants.EnvData.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION") == "true"
|
||||
constants.EnvData.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION") == "true"
|
||||
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = os.Getenv("DISABLE_MAGIC_LINK_LOGIN") == "true"
|
||||
constants.EnvData.DISABLE_LOGIN_PAGE = os.Getenv("DISABLE_LOGIN_PAGE") == "true"
|
||||
|
||||
if constants.SMTP_HOST == "" || constants.SENDER_EMAIL == "" || constants.SENDER_PASSWORD == "" {
|
||||
constants.DISABLE_EMAIL_VERIFICATION = true
|
||||
constants.DISABLE_MAGIC_LINK_LOGIN = true
|
||||
if constants.EnvData.SMTP_HOST == "" || constants.EnvData.SENDER_EMAIL == "" || constants.EnvData.SENDER_PASSWORD == "" {
|
||||
constants.EnvData.DISABLE_EMAIL_VERIFICATION = true
|
||||
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
|
||||
}
|
||||
|
||||
allowedOriginsSplit := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")
|
||||
@@ -205,18 +212,10 @@ func InitEnv() {
|
||||
allowedOrigins = []string{"*"}
|
||||
}
|
||||
|
||||
constants.ALLOWED_ORIGINS = allowedOrigins
|
||||
constants.EnvData.ALLOWED_ORIGINS = allowedOrigins
|
||||
|
||||
if constants.JWT_TYPE == "" {
|
||||
constants.JWT_TYPE = "HS256"
|
||||
}
|
||||
|
||||
if constants.COOKIE_NAME == "" {
|
||||
constants.COOKIE_NAME = "authorizer"
|
||||
}
|
||||
|
||||
if constants.DISABLE_EMAIL_VERIFICATION {
|
||||
constants.DISABLE_MAGIC_LINK_LOGIN = true
|
||||
if constants.EnvData.DISABLE_EMAIL_VERIFICATION {
|
||||
constants.EnvData.DISABLE_MAGIC_LINK_LOGIN = true
|
||||
}
|
||||
|
||||
rolesEnv := strings.TrimSpace(os.Getenv("ROLES"))
|
||||
@@ -256,19 +255,19 @@ func InitEnv() {
|
||||
}
|
||||
}
|
||||
|
||||
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRoleSplit) > 0 {
|
||||
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRolesEnv) > 0 {
|
||||
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
||||
}
|
||||
|
||||
constants.ROLES = roles
|
||||
constants.DEFAULT_ROLES = defaultRoles
|
||||
constants.PROTECTED_ROLES = protectedRoles
|
||||
constants.EnvData.ROLES = roles
|
||||
constants.EnvData.DEFAULT_ROLES = defaultRoles
|
||||
constants.EnvData.PROTECTED_ROLES = protectedRoles
|
||||
|
||||
if os.Getenv("ORGANIZATION_NAME") != "" {
|
||||
constants.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
|
||||
constants.EnvData.ORGANIZATION_NAME = os.Getenv("ORGANIZATION_NAME")
|
||||
}
|
||||
|
||||
if os.Getenv("ORGANIZATION_LOGO") != "" {
|
||||
constants.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
|
||||
constants.EnvData.ORGANIZATION_LOGO = os.Getenv("ORGANIZATION_LOGO")
|
||||
}
|
||||
}
|
||||
|
155
server/env/persist_env.go
vendored
Normal file
155
server/env/persist_env.go
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func PersistEnv() error {
|
||||
config, err := db.Mgr.GetConfig()
|
||||
// config not found in db
|
||||
if err != nil {
|
||||
// AES encryption needs 32 bit key only, so we chop off last 4 characters from 36 bit uuid
|
||||
hash := uuid.New().String()[:36-4]
|
||||
constants.EnvData.ENCRYPTION_KEY = hash
|
||||
encodedHash := utils.EncryptB64(hash)
|
||||
|
||||
configData, err := json.Marshal(constants.EnvData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encryptedConfig, err := utils.EncryptAES(configData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config = db.Config{
|
||||
Hash: encodedHash,
|
||||
Config: encryptedConfig,
|
||||
}
|
||||
|
||||
db.Mgr.AddConfig(config)
|
||||
} else {
|
||||
// decrypt the config data from db
|
||||
// decryption can be done using the hash stored in db
|
||||
encryptionKey := config.Hash
|
||||
decryptedEncryptionKey, err := utils.DecryptB64(encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
constants.EnvData.ENCRYPTION_KEY = decryptedEncryptionKey
|
||||
decryptedConfigs, err := utils.DecryptAES(config.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// temp json to validate with env
|
||||
var jsonData map[string]interface{}
|
||||
|
||||
err = json.Unmarshal(decryptedConfigs, &jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("=> persisted data:", jsonData)
|
||||
|
||||
// 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
|
||||
|
||||
for key, value := range jsonData {
|
||||
fieldType := reflect.TypeOf(value).String()
|
||||
|
||||
// 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
|
||||
envValue := strings.TrimSpace(os.Getenv(key))
|
||||
|
||||
// env is not empty
|
||||
if envValue != "" {
|
||||
// check the type
|
||||
// currently we have 3 types of env vars: string, bool, []string{}
|
||||
if fieldType == "string" {
|
||||
if value != envValue {
|
||||
jsonData[key] = envValue
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
if fieldType == "bool" {
|
||||
newValue := envValue == "true"
|
||||
if value != newValue {
|
||||
jsonData[key] = newValue
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
if fieldType == "[]interface {}" {
|
||||
stringArr := []string{}
|
||||
envStringArr := strings.Split(envValue, ",")
|
||||
for _, v := range value.([]interface{}) {
|
||||
stringArr = append(stringArr, v.(string))
|
||||
}
|
||||
if !utils.IsStringArrayEqual(stringArr, envStringArr) {
|
||||
jsonData[key] = envStringArr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle derivative cases like disabling email verification & magic login
|
||||
// in case SMTP is off but env is set to true
|
||||
if jsonData["SMTP_HOST"] == "" || jsonData["SENDER_EMAIL"] == "" || jsonData["SENDER_PASSWORD"] == "" {
|
||||
if !jsonData["DISABLE_EMAIL_VERIFICATION"].(bool) {
|
||||
jsonData["DISABLE_EMAIL_VERIFICATION"] = true
|
||||
hasChanged = true
|
||||
}
|
||||
|
||||
if !jsonData["DISABLE_MAGIC_LINK_LOGIN"].(bool) {
|
||||
jsonData["DISABLE_MAGIC_LINK_LOGIN"] = true
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("has changed:", hasChanged)
|
||||
if hasChanged {
|
||||
jsonBytes, err := json.Marshal(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonBytes, &constants.EnvData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
configData, err := json.Marshal(constants.EnvData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encryptedConfig, err := utils.EncryptAES(configData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Config = encryptedConfig
|
||||
_, err = db.Mgr.UpdateConfig(config)
|
||||
if err != nil {
|
||||
log.Println("error updating config:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user