Files

93 lines
2.9 KiB
Go
Raw Permalink Normal View History

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
}