Files
Niklas Ye ee25552a53
CI / test (push) Successful in 17s
Release / test (push) Successful in 3s
Release / binaries (push) Successful in 23s
Sign in through the server's single sign-on, with a code
The sign-in screen asks the server how it can be signed in to
(GET /api/auth/config) and offers what it finds: the password form, and
"Sign in with <provider>" when the server can do a device login. The TUI
shows a link and a short code, the person approves it in any browser, and
the next poll hands over the ordinary session, so it works over SSH where
no browser can be opened. The terminal never talks to the identity
provider.

The password form is hidden when the server has turned password login
off. `auth: sso` in config.yaml starts the SSO login straight away, but not
right after signing out, where that would sign the person straight back
in; any other value is refused when the config is read. Polling honours the
server's interval, backs off on slow_down, and gives up after repeated
failures rather than retrying forever.

A server without /api/auth/config answers 404 and is treated as passwords
only, so the sign-in screen is the one it had. Needs terdut-server v0.29.0
for SSO.
2026-09-26 21:47:13 +02:00

85 lines
2.1 KiB
Go

package config
import (
"os"
"path/filepath"
"testing"
)
func writeConfig(t *testing.T, body string) {
t.Helper()
dir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", dir)
if err := os.MkdirAll(filepath.Join(dir, "terdut-tui"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "terdut-tui", "config.yaml"), []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}
func TestLoad_TeamIsOptional(t *testing.T) {
writeConfig(t, "server_url: https://terdut.example.com\n")
cfg, err := Load()
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.Team != "" {
t.Errorf("expected no default team, got %q", cfg.Team)
}
writeConfig(t, "server_url: https://terdut.example.com\nteam: Ops\n")
cfg, err = Load()
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.Team != "Ops" {
t.Errorf("expected team Ops, got %q", cfg.Team)
}
}
// Signing in replaced the API key, so a config that has only a server URL is
// complete, and one that still carries an api_key is noted rather than refused.
func TestLoad_NoAPIKeyNeeded(t *testing.T) {
writeConfig(t, "server_url: https://terdut.example.com\nusername: niklas\n")
cfg, err := Load()
if err != nil {
t.Fatalf("load: %v", err)
}
if cfg.Username != "niklas" || cfg.LegacyAPIKey {
t.Errorf("unexpected config %+v", cfg)
}
writeConfig(t, "server_url: https://terdut.example.com\napi_key: old\n")
cfg, err = Load()
if err != nil {
t.Fatalf("a leftover api_key must not stop the TUI starting: %v", err)
}
if !cfg.LegacyAPIKey {
t.Error("expected the leftover api_key to be noted")
}
}
func TestLoad_AuthIsOptionalAndChecked(t *testing.T) {
for _, tc := range []struct {
yaml, want string
bad bool
}{
{"", "", false},
{"auth: password\n", "password", false},
{"auth: sso\n", "sso", false},
{"auth: oidc\n", "", true},
} {
writeConfig(t, "server_url: https://terdut.example.com\n"+tc.yaml)
cfg, err := Load()
switch {
case tc.bad && err == nil:
t.Errorf("%q: expected an error", tc.yaml)
case !tc.bad && err != nil:
t.Errorf("%q: %v", tc.yaml, err)
case !tc.bad && cfg.Auth != tc.want:
t.Errorf("%q: auth %q, want %q", tc.yaml, cfg.Auth, tc.want)
}
}
}