ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
93 lines
2.9 KiB
Go
93 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Credentials are server-only; policy is stored separately in PostgreSQL.
|
|
type CaptchaClientConfig struct {
|
|
AppID string `json:"captcha_app_id"`
|
|
AppSecretKey string `json:"app_secret_key"`
|
|
}
|
|
type TencentCaptchaConfig struct {
|
|
SecretID string `json:"secret_id"`
|
|
SecretKey string `json:"secret_key"`
|
|
SaaS CaptchaClientConfig `json:"saas"`
|
|
Mini CaptchaClientConfig `json:"mini_program"`
|
|
}
|
|
|
|
func (c *OnboardingConfig) loadTencentCaptchaFile(service string) error {
|
|
if service != "api" && service != "worker" {
|
|
return nil
|
|
}
|
|
path := strings.TrimSpace(os.Getenv("TENCENT_CAPTCHA_CONFIG_FILE"))
|
|
required := path != ""
|
|
if !required {
|
|
path = "/run/secrets/voice-runtime/tencent-captcha.json"
|
|
if !filepath.IsAbs(path) {
|
|
return nil
|
|
}
|
|
}
|
|
if !filepath.IsAbs(path) {
|
|
return errors.New("TENCENT_CAPTCHA_CONFIG_FILE must be absolute")
|
|
}
|
|
f, err := os.Open(filepath.Clean(path))
|
|
if errors.Is(err, os.ErrNotExist) && !required {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return errors.New("Tencent CAPTCHA configuration file unavailable")
|
|
}
|
|
defer f.Close()
|
|
info, err := f.Stat()
|
|
if err != nil || !info.Mode().IsRegular() || info.Size() > 16384 {
|
|
return errors.New("Tencent CAPTCHA configuration must be a regular file of at most 16 KiB")
|
|
}
|
|
raw, err := io.ReadAll(io.LimitReader(f, 16385))
|
|
if err != nil || len(raw) > 16384 {
|
|
return errors.New("Tencent CAPTCHA configuration cannot be read")
|
|
}
|
|
d := json.NewDecoder(bytes.NewReader(bytes.TrimPrefix(raw, []byte{0xef, 0xbb, 0xbf})))
|
|
d.DisallowUnknownFields()
|
|
var in TencentCaptchaConfig
|
|
if d.Decode(&in) != nil || d.Decode(new(any)) != io.EOF {
|
|
return errors.New("Tencent CAPTCHA configuration must match the JSON template")
|
|
}
|
|
if err = in.Validate(); err != nil {
|
|
return err
|
|
}
|
|
c.TencentCaptcha = in
|
|
return nil
|
|
}
|
|
|
|
func (c *TencentCaptchaConfig) Validate() error {
|
|
c.SecretID = strings.TrimSpace(c.SecretID)
|
|
c.SecretKey = strings.TrimSpace(c.SecretKey)
|
|
if (c.SecretID == "") != (c.SecretKey == "") {
|
|
return errors.New("Tencent CAPTCHA requires both SecretId and SecretKey")
|
|
}
|
|
for _, client := range []*CaptchaClientConfig{&c.SaaS, &c.Mini} {
|
|
client.AppID = strings.TrimSpace(client.AppID)
|
|
client.AppSecretKey = strings.TrimSpace(client.AppSecretKey)
|
|
if client.AppID == "" && client.AppSecretKey == "" {
|
|
continue
|
|
}
|
|
id, err := strconv.ParseUint(client.AppID, 10, 64)
|
|
if err != nil || id == 0 || len(client.AppSecretKey) < 16 || len(client.AppSecretKey) > 256 || c.SecretID == "" {
|
|
return errors.New("Tencent CAPTCHA client requires numeric CaptchaAppId, AppSecretKey and API credentials")
|
|
}
|
|
client.AppID = strconv.FormatUint(id, 10)
|
|
}
|
|
if c.SaaS.AppID != "" && c.SaaS.AppID == c.Mini.AppID {
|
|
return errors.New("SaaS and mini-program CAPTCHA must use different CaptchaAppIds")
|
|
}
|
|
return nil
|
|
}
|