3
0
corteza/system/cli/settings.go
Denis Arh aea9741a2b Improve logging, migrate to zap, use requestID
- migrate from log to go.uber.org/zap package
 - add requestID header with sticky log field
 - push logging via context to REST controllers & HTTP middleware
 - enhance request/resnpose logging
 - add service logging framework
 - add ZapProfiler for db query profiling
2019-05-07 21:18:23 +02:00

230 lines
5.8 KiB
Go

package cli
import (
"context"
"encoding/json"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/crusttech/crust/internal/rand"
"github.com/crusttech/crust/internal/settings"
systemService "github.com/crusttech/crust/system/internal/service"
)
func settingsCmd(ctx context.Context, setSvc settings.Service) *cobra.Command {
cmd := &cobra.Command{
Use: "settings",
Short: "Settings management",
}
auto := &cobra.Command{
Use: "auto-configure",
Short: "Run autoconfiguration",
Run: func(cmd *cobra.Command, args []string) {
systemService.DefaultSettings.LoadAuthSettings()
settingsAutoConfigure(
cmd,
setSvc,
cmd.Flags().Lookup("system-api-url").Value.String(),
cmd.Flags().Lookup("auth-frontend-url").Value.String(),
cmd.Flags().Lookup("auth-from-address").Value.String(),
cmd.Flags().Lookup("auth-from-address").Value.String(),
)
},
}
auto.Flags().String("system-api-url", "", "System API URL (http://sytem.api.example.tld)")
auto.Flags().String("auth-frontend-url", "", "http://example.tld")
auto.Flags().String("auth-from-address", "", "name@example.tld")
auto.Flags().String("auth-from-name", "", "Name Surname")
list := &cobra.Command{
Use: "list",
Short: "List all",
Run: func(cmd *cobra.Command, args []string) {
prefix := cmd.Flags().Lookup("prefix").Value.String()
if kv, err := setSvc.FindByPrefix(prefix); err != nil {
exit(cmd, err)
} else {
for _, v := range kv {
cmd.Printf("%s\t%v\n", v.Name, v.Value)
}
}
},
}
list.Flags().String("prefix", "", "Filter settings by prefix")
get := &cobra.Command{
Use: "get [key to get]",
Short: "Get value (raw JSON) for a specific key",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if v, err := setSvc.Get(args[0], 0); err != nil {
exit(cmd, err)
} else if v != nil {
cmd.Printf("%v\n", v.Value)
}
exit(cmd, nil)
},
}
set := &cobra.Command{
Use: "set [key to set] [value",
Short: "Set value (raw JSON) for a specific key",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
value := args[1]
v := &settings.Value{
Name: args[0],
}
if err := v.SetValueAsString(value); err != nil {
exit(cmd, err)
}
exit(cmd, setSvc.Set(v))
},
}
del := &cobra.Command{
Use: "delete [key to remove]",
Short: "Set value (raw JSON) for a specific key",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
exit(cmd, setSvc.Delete(args[0], 0))
},
}
cmd.AddCommand(
auto,
list,
get,
set,
del,
)
return cmd
}
func settingsAutoConfigure(cmd *cobra.Command, setSvc settings.Service, systemApiUrl, frontendUrl, fromAddress, fromName string) {
set := func(name string, value interface{}) {
var (
v *settings.Value
ok bool
err error
)
if setFn, ok := value.(func() interface{}); ok {
// No existing value found
if value = setFn(); value == nil {
// setFn returned nil, skip everything..
return
}
}
// Did we get something else than *settings.Value?
// if so, wrap/marshal it into *settings.Value
if v, ok = value.(*settings.Value); !ok {
v = &settings.Value{Name: name}
if v.Value, err = json.Marshal(value); err != nil {
cmd.Printf("could not marshal setting value: %v", err)
return
}
}
err = setSvc.Set(v)
if err != nil {
cmd.Printf("could not store setting: %v", err)
}
}
setIfMissing := func(name string, value interface{}) {
if existing, err := setSvc.Get(name, 0); err == nil && existing == nil {
set(name, value)
}
}
// Where should external authentication providers redirect to?
setIfMissing("auth.external.redirect-url", func() interface{} {
path := "/auth/external/%s/callback"
if len(systemApiUrl) > 0 {
if strings.Index(systemApiUrl, "http") != 0 {
return "https://" + systemApiUrl + path
} else {
return systemApiUrl + path
}
}
if leHost, has := os.LookupEnv("LETSENCRYPT_HOST"); has {
return "https://" + leHost + path
} else if vHost, has := os.LookupEnv("VIRTUAL_HOST"); has {
return "http://" + vHost + path
} else {
// Fallback to local
return "http://system.api.local.crust.tech" + path
}
})
setIfMissing("auth.external.session-store-secret", func() interface{} {
// Generate session store secret if missing
return string(rand.Bytes(64))
})
setIfMissing("auth.external.session-store-secure", func() interface{} {
// Try to determines if we need secure session store from redirect URL scheme
extRedirUrl, _ := setSvc.GetGlobalString("auth.external.redirect-url")
return strings.Index(extRedirUrl, "https://") > -1
})
if len(frontendUrl) > 0 {
setIfMissing("auth.frontend.url.password-reset", func() interface{} {
return frontendUrl + "/auth/reset-password?token="
})
setIfMissing("auth.frontend.url.email-confirmation", func() interface{} {
return frontendUrl + "/auth/confirm-email?token="
})
setIfMissing("auth.frontend.url.redirect", func() interface{} {
return frontendUrl + "/auth/"
})
}
// Auth email (password reset, email confirmation)
setIfMissing("auth.mail.from-address", func() interface{} {
if len(fromAddress) > 0 {
return fromAddress
}
return "change-me@local.crust.tech"
})
setIfMissing("auth.mail.from-name", func() interface{} {
if len(fromName) > 0 {
return fromName
}
return "Crust"
})
// No external providers preconfigured, so disable
setIfMissing("auth.external.enabled", false)
// Enable internal by default
setIfMissing("auth.internal.enabled", true)
// Enable user creation
setIfMissing("auth.internal.signup.enabled", true)
// No need to confirm email
setIfMissing("auth.internal.signup-email-confirmation-required", false)
// We need password reset
setIfMissing("auth.internal.password-reset.enabled", true)
}