- шаг схемы закрывает поверхность, которую хранилище приносит открытой: собственную регистрацию, вход по паролю и одноразовый код — без этого закрытие приёма обходилось двумя запросами - продление сессии выключено, срок семь суток: иначе отзыв доступа у провайдера до сервиса не доходит никогда - файл записи отдаётся вошедшему по токену файла — пересмотр ADR-2026-08-12-file-link-open-but-not-logged
98 lines
3.5 KiB
Go
98 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Проверка входа — единственная страховка от того, чтобы сервис поднялся с
|
|
// молча выключенным входом, то есть открытым наружу. До этих проверок она не
|
|
// исполнялась ни разу.
|
|
|
|
func validAuthConfig() AuthConfig {
|
|
return AuthConfig{
|
|
AuthURL: "https://auth.example.com/api/oidc/authorization",
|
|
TokenURL: "https://auth.example.com/api/oidc/token",
|
|
UserInfoURL: "https://auth.example.com/api/oidc/userinfo",
|
|
ClientID: "transcriber",
|
|
ClientSecret: "secret-value",
|
|
RedirectURL: "https://transcriber.example.com/auth/callback",
|
|
}
|
|
}
|
|
|
|
func TestAuthConfigValidateAcceptsFilled(t *testing.T) {
|
|
if err := validAuthConfig().Validate(); err != nil {
|
|
t.Fatalf("заполненный конфиг отвергнут: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestAuthConfigValidateNamesEveryMissingKey(t *testing.T) {
|
|
cases := map[string]func(*AuthConfig){
|
|
"auth_url": func(c *AuthConfig) { c.AuthURL = "" },
|
|
"token_url": func(c *AuthConfig) { c.TokenURL = "" },
|
|
"user_info_url": func(c *AuthConfig) { c.UserInfoURL = "" },
|
|
"client_id": func(c *AuthConfig) { c.ClientID = "" },
|
|
"client_secret": func(c *AuthConfig) { c.ClientSecret = "" },
|
|
"redirect_url": func(c *AuthConfig) { c.RedirectURL = "" },
|
|
}
|
|
|
|
for key, clear := range cases {
|
|
t.Run(key, func(t *testing.T) {
|
|
cfg := validAuthConfig()
|
|
clear(&cfg)
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatalf("пустой ключ %s пропущен — сервис поднимется с выключенным входом", key)
|
|
}
|
|
if !strings.Contains(err.Error(), key) {
|
|
t.Fatalf("имя ключа %s не названо: %v", key, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestAuthConfigValidateHidesSecretValue: сообщение об отказе уезжает в журнал,
|
|
// и значения секрета в нём быть не должно — только имя ключа.
|
|
func TestAuthConfigValidateHidesSecretValue(t *testing.T) {
|
|
cfg := validAuthConfig()
|
|
cfg.ClientSecret = "super-secret-value"
|
|
cfg.AuthURL = ""
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatal("отказа нет")
|
|
}
|
|
if strings.Contains(err.Error(), "super-secret-value") {
|
|
t.Fatalf("значение секрета попало в текст отказа: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAuthConfigValidateRejectsMalformedURL: непустая строка, не похожая на
|
|
// адрес, отвергается здесь, а не позже — хранилище отказало бы уже из хука
|
|
// подъёма, до регистрации пробы здоровья, и сервис упал бы молча целиком.
|
|
func TestAuthConfigValidateRejectsMalformedURL(t *testing.T) {
|
|
cases := map[string]string{
|
|
"без схемы": "auth.example.com/api/oidc/authorization",
|
|
"пробел спереди": " https://auth.example.com/authorize",
|
|
"чужая схема": "ftp://auth.example.com/authorize",
|
|
"пустой хост": "https:///authorize",
|
|
"не адрес вовсе": "todo: заполнить",
|
|
}
|
|
|
|
for name, value := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
cfg := validAuthConfig()
|
|
cfg.AuthURL = value
|
|
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatalf("негодный адрес %q пропущен", value)
|
|
}
|
|
if !strings.Contains(err.Error(), "auth_url") {
|
|
t.Fatalf("имя ключа не названо: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|