configs.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package dataprovider
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "fmt"
  19. "image/png"
  20. "net/url"
  21. "golang.org/x/crypto/ssh"
  22. "github.com/drakkan/sftpgo/v2/internal/kms"
  23. "github.com/drakkan/sftpgo/v2/internal/logger"
  24. "github.com/drakkan/sftpgo/v2/internal/util"
  25. )
  26. // Supported values for host keys, KEXs, ciphers, MACs
  27. var (
  28. supportedHostKeyAlgos = []string{ssh.KeyAlgoRSA}
  29. supportedPublicKeyAlgos = []string{ssh.KeyAlgoRSA, ssh.InsecureKeyAlgoDSA}
  30. supportedKexAlgos = []string{
  31. ssh.KeyExchangeDH16SHA512, ssh.InsecureKeyExchangeDH14SHA1, ssh.InsecureKeyExchangeDH1SHA1,
  32. ssh.InsecureKeyExchangeDHGEXSHA1,
  33. }
  34. supportedCiphers = []string{
  35. ssh.InsecureCipherAES128CBC, ssh.InsecureCipherAES192CBC, ssh.InsecureCipherAES256CBC,
  36. ssh.InsecureCipherTripleDESCBC,
  37. }
  38. supportedMACs = []string{
  39. ssh.HMACSHA512ETM, ssh.HMACSHA512,
  40. ssh.InsecureHMACSHA1, ssh.InsecureHMACSHA196,
  41. }
  42. )
  43. // SFTPDConfigs defines configurations for SFTPD
  44. type SFTPDConfigs struct {
  45. HostKeyAlgos []string `json:"host_key_algos,omitempty"`
  46. PublicKeyAlgos []string `json:"public_key_algos,omitempty"`
  47. KexAlgorithms []string `json:"kex_algorithms,omitempty"`
  48. Ciphers []string `json:"ciphers,omitempty"`
  49. MACs []string `json:"macs,omitempty"`
  50. }
  51. func (c *SFTPDConfigs) isEmpty() bool {
  52. if len(c.HostKeyAlgos) > 0 {
  53. return false
  54. }
  55. if len(c.PublicKeyAlgos) > 0 {
  56. return false
  57. }
  58. if len(c.KexAlgorithms) > 0 {
  59. return false
  60. }
  61. if len(c.Ciphers) > 0 {
  62. return false
  63. }
  64. if len(c.MACs) > 0 {
  65. return false
  66. }
  67. return true
  68. }
  69. // GetSupportedHostKeyAlgos returns the supported legacy host key algos
  70. func (*SFTPDConfigs) GetSupportedHostKeyAlgos() []string {
  71. return supportedHostKeyAlgos
  72. }
  73. // GetSupportedPublicKeyAlgos returns the supported legacy public key algos
  74. func (*SFTPDConfigs) GetSupportedPublicKeyAlgos() []string {
  75. return supportedPublicKeyAlgos
  76. }
  77. // GetSupportedKEXAlgos returns the supported KEX algos
  78. func (*SFTPDConfigs) GetSupportedKEXAlgos() []string {
  79. return supportedKexAlgos
  80. }
  81. // GetSupportedCiphers returns the supported ciphers
  82. func (*SFTPDConfigs) GetSupportedCiphers() []string {
  83. return supportedCiphers
  84. }
  85. // GetSupportedMACs returns the supported MACs algos
  86. func (*SFTPDConfigs) GetSupportedMACs() []string {
  87. return supportedMACs
  88. }
  89. func (c *SFTPDConfigs) validate() error {
  90. var hostKeyAlgos []string
  91. for _, algo := range c.HostKeyAlgos {
  92. if algo == ssh.CertAlgoRSAv01 {
  93. continue
  94. }
  95. if !util.Contains(supportedHostKeyAlgos, algo) {
  96. return util.NewValidationError(fmt.Sprintf("unsupported host key algorithm %q", algo))
  97. }
  98. hostKeyAlgos = append(hostKeyAlgos, algo)
  99. }
  100. c.HostKeyAlgos = hostKeyAlgos
  101. var kexAlgos []string
  102. for _, algo := range c.KexAlgorithms {
  103. if algo == "diffie-hellman-group18-sha512" || algo == ssh.KeyExchangeDHGEXSHA256 {
  104. continue
  105. }
  106. if !util.Contains(supportedKexAlgos, algo) {
  107. return util.NewValidationError(fmt.Sprintf("unsupported KEX algorithm %q", algo))
  108. }
  109. kexAlgos = append(kexAlgos, algo)
  110. }
  111. c.KexAlgorithms = kexAlgos
  112. for _, cipher := range c.Ciphers {
  113. if !util.Contains(supportedCiphers, cipher) {
  114. return util.NewValidationError(fmt.Sprintf("unsupported cipher %q", cipher))
  115. }
  116. }
  117. for _, mac := range c.MACs {
  118. if !util.Contains(supportedMACs, mac) {
  119. return util.NewValidationError(fmt.Sprintf("unsupported MAC algorithm %q", mac))
  120. }
  121. }
  122. for _, algo := range c.PublicKeyAlgos {
  123. if !util.Contains(supportedPublicKeyAlgos, algo) {
  124. return util.NewValidationError(fmt.Sprintf("unsupported public key algorithm %q", algo))
  125. }
  126. }
  127. return nil
  128. }
  129. func (c *SFTPDConfigs) getACopy() *SFTPDConfigs {
  130. hostKeys := make([]string, len(c.HostKeyAlgos))
  131. copy(hostKeys, c.HostKeyAlgos)
  132. publicKeys := make([]string, len(c.PublicKeyAlgos))
  133. copy(publicKeys, c.PublicKeyAlgos)
  134. kexs := make([]string, len(c.KexAlgorithms))
  135. copy(kexs, c.KexAlgorithms)
  136. ciphers := make([]string, len(c.Ciphers))
  137. copy(ciphers, c.Ciphers)
  138. macs := make([]string, len(c.MACs))
  139. copy(macs, c.MACs)
  140. return &SFTPDConfigs{
  141. HostKeyAlgos: hostKeys,
  142. PublicKeyAlgos: publicKeys,
  143. KexAlgorithms: kexs,
  144. Ciphers: ciphers,
  145. MACs: macs,
  146. }
  147. }
  148. func validateSMTPSecret(secret *kms.Secret, name string) error {
  149. if secret.IsRedacted() {
  150. return util.NewValidationError(fmt.Sprintf("cannot save a redacted smtp %s", name))
  151. }
  152. if secret.IsEncrypted() && !secret.IsValid() {
  153. return util.NewValidationError(fmt.Sprintf("invalid encrypted smtp %s", name))
  154. }
  155. if !secret.IsEmpty() && !secret.IsValidInput() {
  156. return util.NewValidationError(fmt.Sprintf("invalid smtp %s", name))
  157. }
  158. if secret.IsPlain() {
  159. secret.SetAdditionalData("smtp")
  160. if err := secret.Encrypt(); err != nil {
  161. return util.NewValidationError(fmt.Sprintf("could not encrypt smtp %s: %v", name, err))
  162. }
  163. }
  164. return nil
  165. }
  166. // SMTPOAuth2 defines the SMTP related OAuth2 configurations
  167. type SMTPOAuth2 struct {
  168. Provider int `json:"provider,omitempty"`
  169. Tenant string `json:"tenant,omitempty"`
  170. ClientID string `json:"client_id,omitempty"`
  171. ClientSecret *kms.Secret `json:"client_secret,omitempty"`
  172. RefreshToken *kms.Secret `json:"refresh_token,omitempty"`
  173. }
  174. func (c *SMTPOAuth2) validate() error {
  175. if c.Provider < 0 || c.Provider > 1 {
  176. return util.NewValidationError("smtp oauth2: unsupported provider")
  177. }
  178. if c.ClientID == "" {
  179. return util.NewI18nError(
  180. util.NewValidationError("smtp oauth2: client id is required"),
  181. util.I18nErrorSMTPClientIDRequired,
  182. )
  183. }
  184. if c.ClientSecret == nil {
  185. return util.NewI18nError(
  186. util.NewValidationError("smtp oauth2: client secret is required"),
  187. util.I18nErrorSMTPClientSecretRequired,
  188. )
  189. }
  190. if c.RefreshToken == nil {
  191. return util.NewI18nError(
  192. util.NewValidationError("smtp oauth2: refresh token is required"),
  193. util.I18nErrorSMTPRefreshTokenRequired,
  194. )
  195. }
  196. if err := validateSMTPSecret(c.ClientSecret, "oauth2 client secret"); err != nil {
  197. return err
  198. }
  199. return validateSMTPSecret(c.RefreshToken, "oauth2 refresh token")
  200. }
  201. func (c *SMTPOAuth2) getACopy() SMTPOAuth2 {
  202. var clientSecret, refreshToken *kms.Secret
  203. if c.ClientSecret != nil {
  204. clientSecret = c.ClientSecret.Clone()
  205. }
  206. if c.RefreshToken != nil {
  207. refreshToken = c.RefreshToken.Clone()
  208. }
  209. return SMTPOAuth2{
  210. Provider: c.Provider,
  211. Tenant: c.Tenant,
  212. ClientID: c.ClientID,
  213. ClientSecret: clientSecret,
  214. RefreshToken: refreshToken,
  215. }
  216. }
  217. // SMTPConfigs defines configuration for SMTP
  218. type SMTPConfigs struct {
  219. Host string `json:"host,omitempty"`
  220. Port int `json:"port,omitempty"`
  221. From string `json:"from,omitempty"`
  222. User string `json:"user,omitempty"`
  223. Password *kms.Secret `json:"password,omitempty"`
  224. AuthType int `json:"auth_type,omitempty"`
  225. Encryption int `json:"encryption,omitempty"`
  226. Domain string `json:"domain,omitempty"`
  227. Debug int `json:"debug,omitempty"`
  228. OAuth2 SMTPOAuth2 `json:"oauth2"`
  229. }
  230. // IsEmpty returns true if the configuration is empty
  231. func (c *SMTPConfigs) IsEmpty() bool {
  232. return c.Host == ""
  233. }
  234. func (c *SMTPConfigs) validate() error {
  235. if c.IsEmpty() {
  236. return nil
  237. }
  238. if c.Port <= 0 || c.Port > 65535 {
  239. return util.NewValidationError(fmt.Sprintf("smtp: invalid port %d", c.Port))
  240. }
  241. if c.Password != nil && c.AuthType != 3 {
  242. if err := validateSMTPSecret(c.Password, "password"); err != nil {
  243. return err
  244. }
  245. }
  246. if c.User == "" && c.From == "" {
  247. return util.NewI18nError(
  248. util.NewValidationError("smtp: from address and user cannot both be empty"),
  249. util.I18nErrorSMTPRequiredFields,
  250. )
  251. }
  252. if c.AuthType < 0 || c.AuthType > 3 {
  253. return util.NewValidationError(fmt.Sprintf("smtp: invalid auth type %d", c.AuthType))
  254. }
  255. if c.Encryption < 0 || c.Encryption > 2 {
  256. return util.NewValidationError(fmt.Sprintf("smtp: invalid encryption %d", c.Encryption))
  257. }
  258. if c.AuthType == 3 {
  259. c.Password = kms.NewEmptySecret()
  260. return c.OAuth2.validate()
  261. }
  262. c.OAuth2 = SMTPOAuth2{}
  263. return nil
  264. }
  265. // TryDecrypt tries to decrypt the encrypted secrets
  266. func (c *SMTPConfigs) TryDecrypt() error {
  267. if c.Password == nil {
  268. c.Password = kms.NewEmptySecret()
  269. }
  270. if c.OAuth2.ClientSecret == nil {
  271. c.OAuth2.ClientSecret = kms.NewEmptySecret()
  272. }
  273. if c.OAuth2.RefreshToken == nil {
  274. c.OAuth2.RefreshToken = kms.NewEmptySecret()
  275. }
  276. if err := c.Password.TryDecrypt(); err != nil {
  277. return fmt.Errorf("unable to decrypt smtp password: %w", err)
  278. }
  279. if err := c.OAuth2.ClientSecret.TryDecrypt(); err != nil {
  280. return fmt.Errorf("unable to decrypt smtp oauth2 client secret: %w", err)
  281. }
  282. if err := c.OAuth2.RefreshToken.TryDecrypt(); err != nil {
  283. return fmt.Errorf("unable to decrypt smtp oauth2 refresh token: %w", err)
  284. }
  285. return nil
  286. }
  287. func (c *SMTPConfigs) prepareForRendering() {
  288. if c.Password != nil {
  289. c.Password.Hide()
  290. if c.Password.IsEmpty() {
  291. c.Password = nil
  292. }
  293. }
  294. if c.OAuth2.ClientSecret != nil {
  295. c.OAuth2.ClientSecret.Hide()
  296. if c.OAuth2.ClientSecret.IsEmpty() {
  297. c.OAuth2.ClientSecret = nil
  298. }
  299. }
  300. if c.OAuth2.RefreshToken != nil {
  301. c.OAuth2.RefreshToken.Hide()
  302. if c.OAuth2.RefreshToken.IsEmpty() {
  303. c.OAuth2.RefreshToken = nil
  304. }
  305. }
  306. }
  307. func (c *SMTPConfigs) getACopy() *SMTPConfigs {
  308. var password *kms.Secret
  309. if c.Password != nil {
  310. password = c.Password.Clone()
  311. }
  312. return &SMTPConfigs{
  313. Host: c.Host,
  314. Port: c.Port,
  315. From: c.From,
  316. User: c.User,
  317. Password: password,
  318. AuthType: c.AuthType,
  319. Encryption: c.Encryption,
  320. Domain: c.Domain,
  321. Debug: c.Debug,
  322. OAuth2: c.OAuth2.getACopy(),
  323. }
  324. }
  325. // ACMEHTTP01Challenge defines the configuration for HTTP-01 challenge type
  326. type ACMEHTTP01Challenge struct {
  327. Port int `json:"port"`
  328. }
  329. // ACMEConfigs defines ACME related configuration
  330. type ACMEConfigs struct {
  331. Domain string `json:"domain"`
  332. Email string `json:"email"`
  333. HTTP01Challenge ACMEHTTP01Challenge `json:"http01_challenge"`
  334. // apply the certificate for the specified protocols:
  335. //
  336. // 1 means HTTP
  337. // 2 means FTP
  338. // 4 means WebDAV
  339. //
  340. // Protocols can be combined
  341. Protocols int `json:"protocols"`
  342. }
  343. func (c *ACMEConfigs) isEmpty() bool {
  344. return c.Domain == ""
  345. }
  346. func (c *ACMEConfigs) validate() error {
  347. if c.Domain == "" {
  348. return nil
  349. }
  350. if c.Email == "" && !util.IsEmailValid(c.Email) {
  351. return util.NewI18nError(
  352. util.NewValidationError(fmt.Sprintf("acme: invalid email %q", c.Email)),
  353. util.I18nErrorInvalidEmail,
  354. )
  355. }
  356. if c.HTTP01Challenge.Port <= 0 || c.HTTP01Challenge.Port > 65535 {
  357. return util.NewValidationError(fmt.Sprintf("acme: invalid HTTP-01 challenge port %d", c.HTTP01Challenge.Port))
  358. }
  359. return nil
  360. }
  361. // HasProtocol returns true if the ACME certificate must be used for the specified protocol
  362. func (c *ACMEConfigs) HasProtocol(protocol string) bool {
  363. switch protocol {
  364. case protocolHTTP:
  365. return c.Protocols&1 != 0
  366. case protocolFTP:
  367. return c.Protocols&2 != 0
  368. case protocolWebDAV:
  369. return c.Protocols&4 != 0
  370. default:
  371. return false
  372. }
  373. }
  374. func (c *ACMEConfigs) getACopy() *ACMEConfigs {
  375. return &ACMEConfigs{
  376. Email: c.Email,
  377. Domain: c.Domain,
  378. HTTP01Challenge: ACMEHTTP01Challenge{Port: c.HTTP01Challenge.Port},
  379. Protocols: c.Protocols,
  380. }
  381. }
  382. // BrandingConfig defines the branding configuration
  383. type BrandingConfig struct {
  384. Name string `json:"name"`
  385. ShortName string `json:"short_name"`
  386. Logo []byte `json:"logo"`
  387. Favicon []byte `json:"favicon"`
  388. DisclaimerName string `json:"disclaimer_name"`
  389. DisclaimerURL string `json:"disclaimer_url"`
  390. }
  391. func (c *BrandingConfig) isEmpty() bool {
  392. if c.Name != "" {
  393. return false
  394. }
  395. if c.ShortName != "" {
  396. return false
  397. }
  398. if len(c.Logo) > 0 {
  399. return false
  400. }
  401. if len(c.Favicon) > 0 {
  402. return false
  403. }
  404. if c.DisclaimerName != "" && c.DisclaimerURL != "" {
  405. return false
  406. }
  407. return true
  408. }
  409. func (*BrandingConfig) validatePNG(b []byte, maxWidth, maxHeight int) error {
  410. if len(b) == 0 {
  411. return nil
  412. }
  413. // DecodeConfig is more efficient, but I'm not sure if this would lead to
  414. // accepting invalid images in some edge cases and performance does not
  415. // matter here.
  416. img, err := png.Decode(bytes.NewBuffer(b))
  417. if err != nil {
  418. return util.NewI18nError(
  419. util.NewValidationError("invalid PNG image"),
  420. util.I18nErrorInvalidPNG,
  421. )
  422. }
  423. bounds := img.Bounds()
  424. if bounds.Dx() > maxWidth || bounds.Dy() > maxHeight {
  425. return util.NewI18nError(
  426. util.NewValidationError("invalid PNG image size"),
  427. util.I18nErrorInvalidPNGSize,
  428. )
  429. }
  430. return nil
  431. }
  432. func (c *BrandingConfig) validateDisclaimerURL() error {
  433. if c.DisclaimerURL == "" {
  434. return nil
  435. }
  436. u, err := url.Parse(c.DisclaimerURL)
  437. if err != nil {
  438. return util.NewI18nError(
  439. util.NewValidationError("invalid disclaimer URL"),
  440. util.I18nErrorInvalidDisclaimerURL,
  441. )
  442. }
  443. if u.Scheme != "http" && u.Scheme != "https" {
  444. return util.NewI18nError(
  445. util.NewValidationError("invalid disclaimer URL scheme"),
  446. util.I18nErrorInvalidDisclaimerURL,
  447. )
  448. }
  449. return nil
  450. }
  451. func (c *BrandingConfig) validate() error {
  452. if err := c.validateDisclaimerURL(); err != nil {
  453. return err
  454. }
  455. if err := c.validatePNG(c.Logo, 512, 512); err != nil {
  456. return err
  457. }
  458. return c.validatePNG(c.Favicon, 256, 256)
  459. }
  460. func (c *BrandingConfig) getACopy() BrandingConfig {
  461. logo := make([]byte, len(c.Logo))
  462. copy(logo, c.Logo)
  463. favicon := make([]byte, len(c.Favicon))
  464. copy(favicon, c.Favicon)
  465. return BrandingConfig{
  466. Name: c.Name,
  467. ShortName: c.ShortName,
  468. Logo: logo,
  469. Favicon: favicon,
  470. DisclaimerName: c.DisclaimerName,
  471. DisclaimerURL: c.DisclaimerURL,
  472. }
  473. }
  474. // BrandingConfigs defines the branding configuration for WebAdmin and WebClient UI
  475. type BrandingConfigs struct {
  476. WebAdmin BrandingConfig
  477. WebClient BrandingConfig
  478. }
  479. func (c *BrandingConfigs) isEmpty() bool {
  480. return c.WebAdmin.isEmpty() && c.WebClient.isEmpty()
  481. }
  482. func (c *BrandingConfigs) validate() error {
  483. if err := c.WebAdmin.validate(); err != nil {
  484. return err
  485. }
  486. return c.WebClient.validate()
  487. }
  488. func (c *BrandingConfigs) getACopy() *BrandingConfigs {
  489. return &BrandingConfigs{
  490. WebAdmin: c.WebAdmin.getACopy(),
  491. WebClient: c.WebClient.getACopy(),
  492. }
  493. }
  494. // Configs allows to set configuration keys disabled by default without
  495. // modifying the config file or setting env vars
  496. type Configs struct {
  497. SFTPD *SFTPDConfigs `json:"sftpd,omitempty"`
  498. SMTP *SMTPConfigs `json:"smtp,omitempty"`
  499. ACME *ACMEConfigs `json:"acme,omitempty"`
  500. Branding *BrandingConfigs `json:"branding,omitempty"`
  501. UpdatedAt int64 `json:"updated_at,omitempty"`
  502. }
  503. func (c *Configs) validate() error {
  504. if c.SFTPD != nil {
  505. if err := c.SFTPD.validate(); err != nil {
  506. return err
  507. }
  508. }
  509. if c.SMTP != nil {
  510. if err := c.SMTP.validate(); err != nil {
  511. return err
  512. }
  513. }
  514. if c.ACME != nil {
  515. if err := c.ACME.validate(); err != nil {
  516. return err
  517. }
  518. }
  519. if c.Branding != nil {
  520. if err := c.Branding.validate(); err != nil {
  521. return err
  522. }
  523. }
  524. return nil
  525. }
  526. // PrepareForRendering prepares configs for rendering.
  527. // It hides confidential data and set to nil the empty structs/secrets
  528. // so they are not serialized
  529. func (c *Configs) PrepareForRendering() {
  530. if c.SFTPD != nil && c.SFTPD.isEmpty() {
  531. c.SFTPD = nil
  532. }
  533. if c.SMTP != nil && c.SMTP.IsEmpty() {
  534. c.SMTP = nil
  535. }
  536. if c.ACME != nil && c.ACME.isEmpty() {
  537. c.ACME = nil
  538. }
  539. if c.Branding != nil && c.Branding.isEmpty() {
  540. c.Branding = nil
  541. }
  542. if c.SMTP != nil {
  543. c.SMTP.prepareForRendering()
  544. }
  545. }
  546. // SetNilsToEmpty sets nil fields to empty
  547. func (c *Configs) SetNilsToEmpty() {
  548. if c.SFTPD == nil {
  549. c.SFTPD = &SFTPDConfigs{}
  550. }
  551. if c.SMTP == nil {
  552. c.SMTP = &SMTPConfigs{}
  553. }
  554. if c.SMTP.Password == nil {
  555. c.SMTP.Password = kms.NewEmptySecret()
  556. }
  557. if c.SMTP.OAuth2.ClientSecret == nil {
  558. c.SMTP.OAuth2.ClientSecret = kms.NewEmptySecret()
  559. }
  560. if c.SMTP.OAuth2.RefreshToken == nil {
  561. c.SMTP.OAuth2.RefreshToken = kms.NewEmptySecret()
  562. }
  563. if c.ACME == nil {
  564. c.ACME = &ACMEConfigs{}
  565. }
  566. if c.Branding == nil {
  567. c.Branding = &BrandingConfigs{}
  568. }
  569. }
  570. // RenderAsJSON implements the renderer interface used within plugins
  571. func (c *Configs) RenderAsJSON(reload bool) ([]byte, error) {
  572. if reload {
  573. config, err := provider.getConfigs()
  574. if err != nil {
  575. providerLog(logger.LevelError, "unable to reload config overrides before rendering as json: %v", err)
  576. return nil, err
  577. }
  578. config.PrepareForRendering()
  579. return json.Marshal(config)
  580. }
  581. c.PrepareForRendering()
  582. return json.Marshal(c)
  583. }
  584. func (c *Configs) getACopy() Configs {
  585. var result Configs
  586. if c.SFTPD != nil {
  587. result.SFTPD = c.SFTPD.getACopy()
  588. }
  589. if c.SMTP != nil {
  590. result.SMTP = c.SMTP.getACopy()
  591. }
  592. if c.ACME != nil {
  593. result.ACME = c.ACME.getACopy()
  594. }
  595. if c.Branding != nil {
  596. result.Branding = c.Branding.getACopy()
  597. }
  598. result.UpdatedAt = c.UpdatedAt
  599. return result
  600. }