Remove PROVISION_SETTINGS_ and replace it with importing setting yaml
This commit is contained in:
+22
-169
@@ -2,191 +2,44 @@ package provision
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Discovers "auth.%" settings from the environment
|
||||
// Sets email-related settings (if not set) under "auth.internal..."
|
||||
//
|
||||
// when other kinds of auto-discoverable settings come, lambdas inside will probably need a bit of refactoring
|
||||
func authSettingsAutoDiscovery(ctx context.Context, log *zap.Logger, s store.Storer) (err error) {
|
||||
type (
|
||||
stringWrapper func() string
|
||||
boolWrapper func() bool
|
||||
)
|
||||
|
||||
//
|
||||
func emailSettings(ctx context.Context, s store.Storer) error {
|
||||
var (
|
||||
// Setter
|
||||
//
|
||||
// Finds existing settings, tries with environmental "PROVISION_SETTINGS_AUTH_..." probing
|
||||
// and falls back to default value
|
||||
//
|
||||
// We are extremely verbose here - we want to show all the info available and
|
||||
// how settings were discovered and set
|
||||
//
|
||||
// @todo generalize and move under settings
|
||||
set = func(name string, env string, def interface{}, maskSensitive bool) error {
|
||||
|
||||
var (
|
||||
log = log.With(
|
||||
zap.String("name", name),
|
||||
)
|
||||
|
||||
envExists bool
|
||||
value interface{}
|
||||
|
||||
v, err = s.LookupSettingByNameOwnedBy(ctx, name, 0)
|
||||
)
|
||||
|
||||
if !errors.IsNotFound(err) && err != nil {
|
||||
return fmt.Errorf("could not load settings value for '%s': %w", name, err)
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
// Nothing to discover, already set
|
||||
log.Debug("already set", logger.MaskIf("value", v, maskSensitive))
|
||||
return nil
|
||||
}
|
||||
|
||||
v = &types.SettingValue{Name: name}
|
||||
|
||||
value, envExists = os.LookupEnv(env)
|
||||
|
||||
switch dfn := def.(type) {
|
||||
case stringWrapper:
|
||||
log = log.With(zap.String("type", "string"))
|
||||
// already a string, no need to do any magic
|
||||
if envExists {
|
||||
log = log.With(zap.String("env", env), logger.MaskIf("value", value, maskSensitive))
|
||||
} else {
|
||||
value = dfn()
|
||||
log = log.With(zap.Any("default", value))
|
||||
}
|
||||
case boolWrapper:
|
||||
log = log.With(zap.String("type", "bool"))
|
||||
|
||||
if envExists {
|
||||
value = cast.ToBool(value)
|
||||
log = log.With(zap.String("env", env), zap.Any("value", value))
|
||||
} else {
|
||||
value = dfn()
|
||||
log = log.With(zap.Any("default", value))
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T for '%s'", def, name)
|
||||
}
|
||||
|
||||
if err := v.SetValue(value); err != nil {
|
||||
return fmt.Errorf("could not set value to '%q': %w", name, err)
|
||||
}
|
||||
|
||||
log.Debug("value auto-discovered")
|
||||
return s.UpsertSetting(ctx, v)
|
||||
}
|
||||
|
||||
// Assume we have emailing capabilities if SMTP_HOST variable is set
|
||||
emailCapabilities = func() boolWrapper {
|
||||
return func() bool {
|
||||
val, has := os.LookupEnv("SMTP_HOST")
|
||||
return has && len(val) > 0
|
||||
}
|
||||
}
|
||||
|
||||
wrapBool = func(val bool) boolWrapper {
|
||||
return func() bool { return val }
|
||||
}
|
||||
|
||||
wrapString = func(val string) stringWrapper {
|
||||
return func() string { return val }
|
||||
}
|
||||
val, has = os.LookupEnv("SMTP_HOST")
|
||||
canSendEmails = has && len(val) > 0
|
||||
)
|
||||
|
||||
// List of name-value pairs we need to iterate and set
|
||||
list := []struct {
|
||||
// Setting name
|
||||
nme string
|
||||
|
||||
// provision environmental variable name
|
||||
// we're using full variable name here so developers
|
||||
// can find where things are coming from
|
||||
env string
|
||||
|
||||
// default value
|
||||
// expects one of the *wrapper() functions
|
||||
// this also determinate the value type of the setting and casting rules for the env value
|
||||
def interface{}
|
||||
|
||||
// mask value if sensitive
|
||||
mask bool
|
||||
}{
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// External auth
|
||||
|
||||
// Enable federated auth
|
||||
{
|
||||
"auth.external.enabled",
|
||||
"PROVISION_SETTINGS_AUTH_EXTERNAL_ENABLED",
|
||||
wrapBool(true),
|
||||
false},
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
|
||||
// Auth email
|
||||
{
|
||||
"auth.mail.from-address",
|
||||
"PROVISION_SETTINGS_AUTH_EMAIL_FROM_ADDRESS",
|
||||
wrapString("info@example.tld"),
|
||||
false},
|
||||
|
||||
{
|
||||
"auth.mail.from-name",
|
||||
"PROVISION_SETTINGS_AUTH_EMAIL_FROM_NAME",
|
||||
wrapString("Example Sender"),
|
||||
false},
|
||||
|
||||
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
|
||||
// Enable internal login
|
||||
{
|
||||
"auth.internal.enabled",
|
||||
"PROVISION_SETTINGS_AUTH_INTERNAL_ENABLED",
|
||||
wrapBool(true),
|
||||
false},
|
||||
|
||||
// Enable internal signup
|
||||
{
|
||||
"auth.internal.signup.enabled",
|
||||
"PROVISION_SETTINGS_AUTH_INTERNAL_SIGNUP_ENABLED",
|
||||
wrapBool(true),
|
||||
false},
|
||||
|
||||
// Enable email confirmation if we have email capabilities
|
||||
{
|
||||
ss := types.SettingValueSet{
|
||||
types.MakeSettingValue(
|
||||
"auth.internal.signup.email-confirmation-required",
|
||||
"PROVISION_SETTINGS_AUTH_INTERNAL_SIGNUP_EMAIL_CONFIRMATION_REQUIRED",
|
||||
emailCapabilities(),
|
||||
false},
|
||||
canSendEmails,
|
||||
),
|
||||
|
||||
// Enable password reset if we have email capabilities
|
||||
{
|
||||
types.MakeSettingValue(
|
||||
"auth.internal.password-reset.enabled",
|
||||
"PROVISION_SETTINGS_AUTH_INTERNAL_PASSWORD_RESET_ENABLED",
|
||||
emailCapabilities(),
|
||||
false},
|
||||
canSendEmails,
|
||||
),
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
if err = set(item.nme, item.env, item.def, item.mask); err != nil {
|
||||
return s.Tx(ctx, func(ctx context.Context, s store.Storer) error {
|
||||
return ss.Walk(func(setting *types.SettingValue) error {
|
||||
_, err := store.LookupSettingByNameOwnedBy(ctx, s, setting.Name, 0)
|
||||
if errors.IsNotFound(err) {
|
||||
setting.UpdatedAt = *now()
|
||||
return store.CreateSetting(ctx, s, setting)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti
|
||||
func() error { return importConfig(ctx, log.Named("config"), s, provisionOpt.Path) },
|
||||
|
||||
// Auto-discoveries and other parts that cannot be imported from static files
|
||||
func() error { return authSettingsAutoDiscovery(ctx, log.Named("auth.settings-discovery"), s) },
|
||||
func() error { return emailSettings(ctx, s) },
|
||||
func() error { return authAddExternals(ctx, log.Named("auth.externals"), s) },
|
||||
func() error { return oidcAutoDiscovery(ctx, log.Named("auth.oidc-auto-discovery"), s, authOpt) },
|
||||
func() error { return defaultAuthClient(ctx, log.Named("auth.clients"), s, authOpt) },
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
settings:
|
||||
auth.external.enabled: true
|
||||
auth.mail.from-address: info@example.tld
|
||||
auth.mail.from-name: Corteza
|
||||
auth.internal.enabled: true
|
||||
auth.internal.signup.enabled: true
|
||||
|
||||
# We add this through provision procedures
|
||||
# if we detect valid SMTP settings
|
||||
# auth.internal.signup.email-confirmation-required: true
|
||||
|
||||
# We add this through provision procedures
|
||||
# if we detect valid SMTP settings
|
||||
# auth.internal.password-reset.enabled: true
|
||||
@@ -41,6 +41,12 @@ const (
|
||||
settingsFilterPerPageMax = 100
|
||||
)
|
||||
|
||||
func MakeSettingValue(name string, value interface{}) *SettingValue {
|
||||
o := &SettingValue{Name: name}
|
||||
_ = o.SetValue(value)
|
||||
return o
|
||||
}
|
||||
|
||||
func (v *SettingValue) SetRawValue(str string) error {
|
||||
var dummy interface{}
|
||||
// Test input to be sure we can save it...
|
||||
@@ -60,7 +66,7 @@ func (v *SettingValue) SetValue(value interface{}) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Value = buf.Bytes()
|
||||
v.Value = bytes.TrimSpace(buf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user