package config

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/apache/trafficcontrol/v8/lib/go-log"
	"github.com/apache/trafficcontrol/v8/lib/go-rfc"
	"github.com/apache/trafficcontrol/v8/lib/go-util"
)

// Options is a structure used to hold the route configuration options that can be supplied for the backend routes.
type Options struct {
	Algorithm string `json:"alg"`
}

// Host is a structure that holds the host info for the backend route.
type Host struct {
	Protocol string `json:"protocol"`
	Hostname string `json:"hostname"`
	Port     int    `json:"port"`
}

// BackendRoute holds all the information about a configured route, for which Traffic Ops serves as a reverse proxy.
type BackendRoute struct {
	Path        string   `json:"path"`
	Method      string   `json:"method"`
	Hosts       []Host   `json:"hosts"`
	Opts        Options  `json:"opts"`
	ID          int      `json:"routeId"`
	Insecure    bool     `json:"insecure"`
	Permissions []string `json:"permissions"`
	Index       int
}

// BackendConfig is a structure that holds the configuration supplied to Traffic Ops, which makes it act as a reverse proxy to the specified routes.
type BackendConfig struct {
	Routes []BackendRoute `json:"routes"`
}

// Config reflects the structure of the cdn.conf file
type Config struct {
	URL                                       *url.URL `json:"-"`
	CertPath                                  string   `json:"-"`
	KeyPath                                   string   `json:"-"`
	ConfigTrafficOpsGolang                    `json:"traffic_ops_golang"`
	ConfigTO                                  *ConfigTO   `json:"to"`
	SMTP                                      *ConfigSMTP `json:"smtp"`
	ConfigPortal                              `json:"portal"`
	ConfigLetsEncrypt                         `json:"lets_encrypt"`
	ConfigAcmeRenewal                         `json:"acme_renewal"`
	AcmeAccounts                              []ConfigAcmeAccount `json:"acme_accounts"`
	DB                                        ConfigDatabase      `json:"db"`
	Secrets                                   []string            `json:"secrets"`
	TrafficVaultEnabled                       bool
	ConfigLDAP                                *ConfigLDAP
	UserCacheRefreshIntervalSec               int `json:"user_cache_refresh_interval_sec"`
	ServerUpdateStatusCacheRefreshIntervalSec int `json:"server_update_status_cache_refresh_interval_sec"`
	LDAPEnabled                               bool
	LDAPConfPath                              string `json:"ldap_conf_location"`
	ConfigInflux                              *ConfigInflux
	InfluxEnabled                             bool
	InfluxDBConfPath                          string `json:"influxdb_conf_path"`
	Version                                   string
	DisableAutoCertDeletion                   bool                    `json:"disable_auto_cert_deletion"`
	UseIMS                                    bool                    `json:"use_ims"`
	RoleBasedPermissions                      bool                    `json:"role_based_permissions"`
	DefaultCertificateInfo                    *DefaultCertificateInfo `json:"default_certificate_info"`
	Cdni                                      *CdniConf               `json:"cdni"`
	ClientCertAuth                            *ClientCertAuth         `json:"client_certificate_authentication"`
}

// ConfigTrafficOpsGolang carries settings specific to traffic_ops_golang server
type ConfigTrafficOpsGolang struct {
	// Deprecated in 5.0
	Insecure bool `json:"insecure"`
	// end deprecated
	//Moved from Hypnotoad-listen section
	Cert                     string                     `json:"cert"`
	Key                      string                     `json:"key"`
	Port                     string                     `json:"port"`
	ProxyTimeout             int                        `json:"proxy_timeout"`
	ProxyKeepAlive           int                        `json:"proxy_keep_alive"`
	ProxyTLSTimeout          int                        `json:"proxy_tls_timeout"`
	ProxyReadHeaderTimeout   int                        `json:"proxy_read_header_timeout"`
	ReadTimeout              int                        `json:"read_timeout"`
	RequestTimeout           int                        `json:"request_timeout"`
	ReadHeaderTimeout        int                        `json:"read_header_timeout"`
	WriteTimeout             int                        `json:"write_timeout"`
	IdleTimeout              int                        `json:"idle_timeout"`
	LogLocationError         string                     `json:"log_location_error"`
	LogLocationWarning       string                     `json:"log_location_warning"`
	LogLocationInfo          string                     `json:"log_location_info"`
	LogLocationDebug         string                     `json:"log_location_debug"`
	LogLocationEvent         string                     `json:"log_location_event"`
	MaxDBConnections         int                        `json:"max_db_connections"`
	DBMaxIdleConnections     int                        `json:"db_max_idle_connections"`
	DBConnMaxLifetimeSeconds int                        `json:"db_conn_max_lifetime_seconds"`
	DBQueryTimeoutSeconds    int                        `json:"db_query_timeout_seconds"`
	Plugins                  []string                   `json:"plugins"`
	PluginConfig             map[string]json.RawMessage `json:"plugin_config"`
	PluginSharedConfig       map[string]interface{}     `json:"plugin_shared_config"`
	ProfilingEnabled         bool                       `json:"profiling_enabled"`
	ProfilingLocation        string                     `json:"profiling_location"`
	// Deprecated: use 'port' in traffic_vault_config instead.
	RiakPort             *uint    `json:"riak_port"`
	WhitelistedOAuthUrls []string `json:"whitelisted_oauth_urls"`
	OAuthClientSecret    string   `json:"oauth_client_secret"`
	OAuthUserAttribute   string   `json:"oauth_user_attribute"`
	RoutingBlacklist     `json:"routing_blacklist"`
	SupportedDSMetrics   []string        `json:"supported_ds_metrics"`
	TLSConfig            *tls.Config     `json:"tls_config"`
	TrafficVaultBackend  string          `json:"traffic_vault_backend"`
	TrafficVaultConfig   json.RawMessage `json:"traffic_vault_config"`

	// CRConfigUseRequestHost is whether to use the client request host header in the CRConfig. If false, uses the tm.url parameter.
	// This defaults to false. Traffic Ops used to always use the host header, setting this true will resume that legacy behavior.
	// See https://github.com/apache/trafficcontrol/issues/2224
	// Deprecated: will be removed in the next major version.
	CRConfigUseRequestHost bool `json:"crconfig_snapshot_use_client_request_host"`
	// CRConfigEmulateOldPath is whether to emulate the legacy CRConfig request path when generating a new CRConfig. This primarily exists in the event a tool relies on the legacy path '/tools/write_crconfig'.
	// Deprecated: will be removed in the next major version.
	CRConfigEmulateOldPath bool `json:"crconfig_emulate_old_path"`
}

// RoutingBlacklist contains a list of route IDs that are disabled,
// and whether or not to ignore unknown routes.
type RoutingBlacklist struct {
	IgnoreUnknownRoutes bool  `json:"ignore_unknown_routes"`
	DisabledRoutes      []int `json:"disabled_routes"`
}

// ConfigTO contains information to identify Traffic Ops in a network sense.
type ConfigTO struct {
	BaseURL               *rfc.URL          `json:"base_url"`
	EmailFrom             *rfc.EmailAddress `json:"email_from"`
	NoAccountFoundMessage *string           `json:"no_account_found_msg"`
}

// ConfigPortal contains information that can direct users to a friendly UI
type ConfigPortal struct {
	BaseURL          rfc.URL          `json:"base_url"`
	DocsURL          rfc.URL          `json:"docs_url"`
	EmailFrom        rfc.EmailAddress `json:"email_from"`
	PasswdResetPath  string           `json:"pass_reset_path"`
	UserRegisterPath string           `json:"user_register_path"`
}

// ConfigSMTP contains configuration information for connecting to and authenticating with an SMTP
// server.
type ConfigSMTP struct {
	Address  string `json:"address"`
	Enabled  bool   `json:"enabled"`
	Password string `json:"password"`
	User     string `json:"user"`
}

// ConfigLetsEncrypt contains configuration information for integration with the Let's Encrypt certificate authority.
type ConfigLetsEncrypt struct {
	Email                     string `json:"user_email,omitempty"`
	SendExpEmail              bool   `json:"send_expiration_email"`
	ConvertSelfSigned         bool   `json:"convert_self_signed"`
	RenewDaysBeforeExpiration int    `json:"renew_days_before_expiration"`
	Environment               string `json:"environment"`
}

// ConfigAcmeRenewal continas configuration information for automated ACME renewals.
type ConfigAcmeRenewal struct {
	SummaryEmail              string `json:"summary_email"`
	RenewDaysBeforeExpiration int    `json:"renew_days_before_expiration"`
}

// ConfigAcmeAccount contains all account information for a single ACME provider to be registered with External Account Binding
type ConfigAcmeAccount struct {
	AcmeProvider string `json:"acme_provider"`
	UserEmail    string `json:"user_email"`
	AcmeUrl      string `json:"acme_url"`
	Kid          string `json:"kid"`
	HmacEncoded  string `json:"hmac_encoded"`
}

type DefaultCertificateInfo struct {
	BusinessUnit string `json:"business_unit"`
	City         string `json:"city"`
	Organization string `json:"organization"`
	Country      string `json:"country"`
	State        string `json:"state"`
}

func (d *DefaultCertificateInfo) Validate() (error, bool) {
	missingList := []string{}
	if d.BusinessUnit == "" {
		missingList = append(missingList, "BusinessUnit")
	}
	if d.City == "" {
		missingList = append(missingList, "City")
	}
	if d.Organization == "" {
		missingList = append(missingList, "Organization")
	}
	if d.Country == "" {
		missingList = append(missingList, "Country")
	}
	if d.State == "" {
		missingList = append(missingList, "State")
	}

	if len(missingList) != 0 {
		return fmt.Errorf("default certificate information is missing: %s", missingList), false
	}
	return nil, true
}

// ConfigDatabase reflects the structure of the database.conf file
type ConfigDatabase struct {
	Description string `json:"description"`
	DBName      string `json:"dbname"`
	Hostname    string `json:"hostname"`
	User        string `json:"user"`
	Password    string `json:"password"`
	Port        string `json:"port"`
	Type        string `json:"type"`
	SSL         bool   `json:"ssl"`
}

type ConfigLDAP struct {
	AdminPass       string `json:"admin_pass"`
	SearchBase      string `json:"search_base"`
	AdminDN         string `json:"admin_dn"`
	Host            string `json:"host"`
	SearchQuery     string `json:"search_query"`
	Insecure        bool   `json:"insecure"`
	LDAPTimeoutSecs int    `json:"ldap_timeout_secs"`
}

type ConfigInflux struct {
	User        string `json:"user"`
	Password    string `json:"password"`
	DSDBName    string `json:"deliveryservice_stats_db_name"`
	CacheDBName string `json:"cache_stats_db_name"`
	Secure      *bool  `json:"secure"`
}

type CdniConf struct {
	DCdnId string `json:"dcdn_id"`
}

type ClientCertAuth struct {
	RootCertsDir string `json:"root_certificates_directory"`
}

// NewFakeConfig returns a fake Config struct with just enough data to view Routes.
func NewFakeConfig() Config {
	c := Config{}
	c.URL, _ = url.Parse("http://example.com")
	c.Secrets = append(c.Secrets, "foo")
	return c
}

const (
	DefaultLDAPTimeoutSecs    = 60
	DefaultDBQueryTimeoutSecs = 20
	DefaultDBPort             = "5432"
	MinPort                   = 1
	MaxPort                   = 65535
)

// ErrorLog - critical messages
func (c Config) ErrorLog() log.LogLocation {
	return log.LogLocation(c.LogLocationError)
}

// WarningLog - warning messages
func (c Config) WarningLog() log.LogLocation {
	return log.LogLocation(c.LogLocationWarning)
}

// InfoLog - information messages
func (c Config) InfoLog() log.LogLocation { return log.LogLocation(c.LogLocationInfo) }

// DebugLog - troubleshooting messages
func (c Config) DebugLog() log.LogLocation {
	return log.LogLocation(c.LogLocationDebug)
}

// EventLog - access.log high level transactions
func (c Config) EventLog() log.LogLocation {
	return log.LogLocation(c.LogLocationEvent)
}

const BlockStartup = true
const AllowStartup = false

func LoadBackendConfig(backendConfigPath string) (BackendConfig, error) {
	confBytes, err := ioutil.ReadFile(backendConfigPath)
	if err != nil {
		return BackendConfig{}, fmt.Errorf("reading backend conf '%s': %v", backendConfigPath, err)
	}

	cfg := BackendConfig{}
	err = json.Unmarshal(confBytes, &cfg)
	if err != nil {
		return BackendConfig{}, fmt.Errorf("unmarshalling '%s': %v", backendConfigPath, err)
	}
	for _, r := range cfg.Routes {
		if r.Opts.Algorithm != "" && r.Opts.Algorithm != "roundrobin" {
			return cfg, errors.New("algorithm can only be roundrobin or blank")
		}
		for _, h := range r.Hosts {
			rawURL := h.Protocol + "://" + h.Hostname + ":" + strconv.Itoa(h.Port)
			if _, err = url.ParseRequestURI(rawURL); err != nil {
				return cfg, fmt.Errorf("couldn't convert host info into a valid URI: %v", err)
			}
		}
	}
	return cfg, nil
}

func LoadCdnConfig(cdnConfPath string) (Config, error) {
	// load json from cdn.conf
	confBytes, err := ioutil.ReadFile(cdnConfPath)
	if err != nil {
		return Config{}, fmt.Errorf("reading CDN conf '%s': %v", cdnConfPath, err)
	}

	cfg := Config{}
	err = json.Unmarshal(confBytes, &cfg)
	if err != nil {
		return Config{}, fmt.Errorf("unmarshalling '%s': %v", cdnConfPath, err)
	}
	if cfg.SMTP == nil {
		cfg.SMTP = &ConfigSMTP{}
	}
	return cfg, nil
}

// LoadConfig - reads the config file into the Config struct

func LoadConfig(cdnConfPath string, dbConfPath string, appVersion string) (Config, []error, bool) {
	// load cdn.conf
	cfg, err := LoadCdnConfig(cdnConfPath)
	if err != nil {
		return Config{}, []error{fmt.Errorf("Loading cdn config from '%s': %v", cdnConfPath, err)}, BlockStartup
	}
	cfg.Version = appVersion

	// load json from database.conf
	dbConfBytes, err := ioutil.ReadFile(dbConfPath)
	if err != nil {
		return Config{}, []error{fmt.Errorf("reading db conf '%s': %v", dbConfPath, err)}, BlockStartup
	}
	err = json.Unmarshal(dbConfBytes, &cfg.DB)
	if err != nil {
		return Config{}, []error{fmt.Errorf("unmarshalling '%s': %v", dbConfPath, err)}, BlockStartup
	}
	if portNum, err := strconv.Atoi(cfg.DB.Port); err != nil || portNum < MinPort || MaxPort < portNum {
		_, _ = fmt.Fprintf(os.Stderr, "error parsing database port: '%s' is invalid. Using default %s\n", cfg.DB.Port, DefaultDBPort)
		cfg.DB.Port = DefaultDBPort
	}
	cfg, err = ParseConfig(cfg)
	if err != nil {
		return Config{}, []error{fmt.Errorf("parsing config '%s': %v", cdnConfPath, err)}, BlockStartup
	}

	// check for and load ldap.conf
	if cfg.LDAPConfPath != "" {
		cfg.LDAPEnabled, cfg.ConfigLDAP, err = GetLDAPConfig(cfg.LDAPConfPath)
		if err != nil {
			cfg.LDAPEnabled = false
			return cfg, []error{fmt.Errorf("parsing ldap config '%s': %v", cfg.LDAPConfPath, err)}, BlockStartup
		}
	} else { // ldap config location not specified in cdn.conf, check in directory with cdn.conf for backwards compatibility with perl.
		confDir := filepath.Dir(cdnConfPath)
		genericLDAPConfPath := filepath.Join(confDir, "ldap.conf")
		if _, err := os.Stat(genericLDAPConfPath); !os.IsNotExist(err) { // ldap.conf exists and we should error if it is not readable/parseable.
			cfg.LDAPEnabled, cfg.ConfigLDAP, err = GetLDAPConfig(genericLDAPConfPath)
			if err != nil { // no config or unparseable, do not enable LDAP
				cfg.LDAPEnabled = false
				return cfg, []error{err}, BlockStartup
			}
		} else {
			cfg.LDAPEnabled = false // no ldap.conf, disable and allow startup
		}
	}

	idbPath := cfg.InfluxDBConfPath
	if idbPath == "" {
		mojoMode := os.Getenv("MOJO_MODE")

		if cwd, err := os.Getwd(); mojoMode != "" && err != nil {
			idbPath = filepath.Join(cwd, "conf", mojoMode, "influxdb.conf")
		} else {
			idbPath = filepath.Join(filepath.Dir(cdnConfPath), "influxdb.conf")
		}
	}

	if _, err = os.Stat(idbPath); err != nil {
		if os.IsNotExist(err) {
			cfg.InfluxEnabled = false
		} else {
			return cfg, []error{err}, BlockStartup
		}
	} else if cfg.InfluxEnabled, cfg.ConfigInflux, err = GetInfluxConfig(idbPath); err != nil {
		return cfg, []error{err}, BlockStartup
	}

	return cfg, []error{}, AllowStartup
}

// GetCertPath - get the path to cert .cert file
func (c Config) GetCertPath() string {
	cert := c.Cert
	if cert != "" {
		return cert
	}
	return ""
}

// GetKeyPath - get the path to cert .key file
func (c Config) GetKeyPath() string {
	key := c.Key
	if key != "" {
		return key
	}
	return ""
}

const (
	DBMaxIdleConnectionsDefault     = 10 // if this is higher than MaxDBConnections it will be automatically adjusted below it by the db/sql library
	DBConnMaxLifetimeSecondsDefault = 60
)

// ParseConfig validates required fields, and parses non-JSON types
func ParseConfig(cfg Config) (Config, error) {
	missings := ""
	if cfg.Cert == "" {
		missings += `"cert", `
	}
	if cfg.Key == "" {
		missings += `"key", `
	}
	if cfg.Port == "" {
		missings += "port, "
	}
	if len(cfg.Secrets) == 0 {
		missings += "secrets, "
	}
	if cfg.LogLocationError == "" {
		cfg.LogLocationError = log.LogLocationNull
	}
	if cfg.LogLocationWarning == "" {
		cfg.LogLocationWarning = log.LogLocationNull
	}
	if cfg.LogLocationInfo == "" {
		cfg.LogLocationInfo = log.LogLocationNull
	}
	if cfg.LogLocationDebug == "" {
		cfg.LogLocationDebug = log.LogLocationNull
	}
	if cfg.LogLocationEvent == "" {
		cfg.LogLocationEvent = log.LogLocationNull
	}
	if cfg.DBMaxIdleConnections == 0 {
		cfg.DBMaxIdleConnections = DBMaxIdleConnectionsDefault
	}
	if cfg.DBConnMaxLifetimeSeconds == 0 {
		cfg.DBConnMaxLifetimeSeconds = DBConnMaxLifetimeSecondsDefault
	}
	if cfg.DBQueryTimeoutSeconds == 0 {
		cfg.DBQueryTimeoutSeconds = DefaultDBQueryTimeoutSecs
	}
	if cfg.UserCacheRefreshIntervalSec < 0 {
		cfg.UserCacheRefreshIntervalSec = 0
	}
	if cfg.ServerUpdateStatusCacheRefreshIntervalSec < 0 {
		cfg.ServerUpdateStatusCacheRefreshIntervalSec = 0
	}

	invalidTOURLStr := ""
	var err error
	rawURL := fmt.Sprintf("cert=%s&key=%s", cfg.Cert, cfg.Key)
	if cfg.URL, err = url.Parse(rawURL); err != nil {
		invalidTOURLStr = fmt.Sprintf("invalid Traffic Ops URL '%s': %v", rawURL, err)
	}
	cfg.KeyPath = cfg.GetKeyPath()
	cfg.CertPath = cfg.GetCertPath()

	newURL := url.URL{Scheme: "https", Host: cfg.URL.Host}
	cfg.URL = &newURL

	if cfg.ConfigTO == nil {
		missings += "to, "
	} else {
		if cfg.ConfigTO.BaseURL == nil || cfg.ConfigTO.BaseURL.String() == "" {
			missings += "to.base_url, "
		}
		if cfg.ConfigTO.EmailFrom == nil || cfg.ConfigTO.EmailFrom.String() == "<@>" {
			missings += "to.email_from, "
		}
		if cfg.ConfigTO.NoAccountFoundMessage == nil || *cfg.ConfigTO.NoAccountFoundMessage == "" {
			missings += "to.no_account_found_msg, "
		}
	}

	if len(missings) > 0 {
		missings = "missing fields: " + missings[:len(missings)-2] // strip final `, `
	}

	errStr := missings
	if errStr != "" && invalidTOURLStr != "" {
		errStr += "; "
	}
	errStr += invalidTOURLStr
	if err := ValidateRoutingBlacklist(cfg.RoutingBlacklist); err != nil {
		return Config{}, err
	}

	return cfg, nil
}

func ValidateRoutingBlacklist(blacklist RoutingBlacklist) error {
	seenDisabledIDs := make(map[int]struct{}, len(blacklist.DisabledRoutes))
	for _, id := range blacklist.DisabledRoutes {
		if _, found := seenDisabledIDs[id]; !found {
			seenDisabledIDs[id] = struct{}{}
		} else {
			return fmt.Errorf("route ID %d is listed multiple times in disabled_routes", id)
		}
	}
	return nil
}

func GetLDAPConfig(LDAPConfPath string) (bool, *ConfigLDAP, error) {
	LDAPConfBytes, err := ioutil.ReadFile(LDAPConfPath)
	if err != nil {
		return false, nil, fmt.Errorf("reading LDAP conf '%v': %v", LDAPConfPath, err)
	}
	LDAPconf, err := getLDAPConf(string(LDAPConfBytes))
	if err != nil {
		return false, LDAPconf, fmt.Errorf("parsing LDAP conf '%v': %v", LDAPConfBytes, err)
	}
	if strings.TrimSpace(LDAPconf.AdminPass) == "" {
		return false, LDAPconf, fmt.Errorf("LDAP conf missing admin_pass field")
	}
	if strings.TrimSpace(LDAPconf.SearchBase) == "" {
		return false, LDAPconf, fmt.Errorf("LDAP conf missing search_base field")
	}
	if strings.TrimSpace(LDAPconf.AdminDN) == "" {
		return false, LDAPconf, fmt.Errorf("LDAP conf missing admin_dn field")
	}
	if strings.TrimSpace(LDAPconf.Host) == "" {
		return false, LDAPconf, fmt.Errorf("LDAP conf missing host field")
	}
	if strings.TrimSpace(LDAPconf.SearchQuery) == "" {
		return false, LDAPconf, fmt.Errorf("LDAP conf missing search_query field")
	}

	return true, LDAPconf, nil
}

func GetInfluxConfig(path string) (bool, *ConfigInflux, error) {
	raw, err := ioutil.ReadFile(path)
	if err != nil {
		return false, nil, fmt.Errorf("reading InfluxDB configuration from '%s': %v", path, err)
	}

	c := ConfigInflux{}
	if err := json.Unmarshal(raw, &c); err != nil {
		return false, nil, fmt.Errorf("parsing InfluxDB configuration from '%s': %v", path, err)
	}

	if c.User == "" {
		return false, &c, errors.New("InfluxDB configuration missing a username")
	}

	if c.Password == "" {
		return false, &c, errors.New("InfluxDB configuration missing a password")
	}

	if c.DSDBName == "" {
		log.Warnln("InfluxDB configuration does not specify a DS stats DB name - falling back on 'deliveryservice_stats'")
		c.DSDBName = "deliveryservice_stats"
	}

	if c.CacheDBName == "" {
		log.Warnln("InfluxDB configuration does not specify a Cache Stats DB name - falling back on 'cache_stats'")
		c.CacheDBName = "cache_stats"
	}

	if c.Secure == nil {
		log.Warnln("InfluxDB configuration does not specify 'secure', defaulting to 'false'")
		c.Secure = util.BoolPtr(false)
	}

	return true, &c, nil
}

func getLDAPConf(s string) (*ConfigLDAP, error) {
	ldapConf := ConfigLDAP{LDAPTimeoutSecs: DefaultLDAPTimeoutSecs} //if the field is not set in the config we use the default instead of 0
	err := json.Unmarshal([]byte(s), &ldapConf)
	return &ldapConf, err
}
