initial commit on Federted servers
release gate / check (push) Failing after 8m35s

This commit is contained in:
Andrew S Erwin
2026-10-19 16:23:55 -05:00
commit b9721ff13f
217 changed files with 31534 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
package config
import (
"net/url"
"os"
"strings"
)
// DBURL returns the database connection string, preferring DATABASE_URL and
// falling back to DB_URL (used by the Makefile).
func DBURL() string {
if v := os.Getenv("DATABASE_URL"); v != "" {
return v
}
return os.Getenv("DB_URL")
}
// Config holds the runtime configuration, loaded from environment variables.
// See porting guide section 7.
type Config struct {
DatabaseURL string
NextAuthSecret string // == AUTH_SECRET; signs sessions and derives IMAP key
AuthSecret string
NextAuthURL string
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
AdminEmails []string
}
// Load reads configuration from the environment. Placeholders keep OIDC off.
func Load() *Config {
return &Config{
DatabaseURL: DBURL(),
NextAuthSecret: os.Getenv("NEXTAUTH_SECRET"),
AuthSecret: os.Getenv("AUTH_SECRET"),
NextAuthURL: os.Getenv("NEXTAUTH_URL"),
OIDCIssuer: os.Getenv("OIDC_ISSUER"),
OIDCClientID: os.Getenv("OIDC_CLIENT_ID"),
OIDCClientSecret: os.Getenv("OIDC_CLIENT_SECRET"),
AdminEmails: AdminEmails(),
}
}
// Secret returns the signing secret, preferring NEXTAUTH_SECRET then AUTH_SECRET.
// The original code falls back to a hard-coded string; a port must fail instead.
func (c *Config) Secret() string {
if c.NextAuthSecret != "" {
return c.NextAuthSecret
}
return c.AuthSecret
}
// EncryptionSecret returns the key material for at-rest encryption of
// integration credentials (review OPS-01). A dedicated ENCRYPTION_KEY lets
// operators rotate AUTH_SECRET (sessions) without invalidating stored IMAP
// passwords. Without it the auth secret remains the key, matching the
// original derivation, and stored rows keep decrypting via the legacy
// fallback in signals.LoadMailbox.
func (c *Config) EncryptionSecret() string {
if v := os.Getenv("ENCRYPTION_KEY"); v != "" {
return v
}
return c.Secret()
}
// CookieSecure reports whether session cookies must carry the Secure flag.
// An explicit COOKIE_SECURE setting wins; otherwise the NEXTAUTH_URL scheme
// decides, so TLS deployments are secure by default without knowing about
// the extra variable.
func (c *Config) CookieSecure() bool {
if v := os.Getenv("COOKIE_SECURE"); v != "" {
return v == "1"
}
u, err := url.Parse(strings.TrimSpace(c.NextAuthURL))
return err == nil && u.Scheme == "https"
}
// AdminEmails splits env ADMIN_EMAILS on commas, trims, lowercases.
func AdminEmails() []string {
s := os.Getenv("ADMIN_EMAILS")
if s == "" {
return nil
}
var out []string
for _, email := range strings.Split(s, ",") {
if email = strings.ToLower(strings.TrimSpace(email)); email != "" {
out = append(out, email)
}
}
return out
}
// IsAdminEmail reports whether email is listed in ADMIN_EMAILS.
func IsAdminEmail(email string) bool {
e := strings.ToLower(strings.TrimSpace(email))
for _, a := range AdminEmails() {
if strings.ToLower(a) == e {
return true
}
}
return false
}
// AllowBootstrapSignup reports whether the first self-service signup on a
// fresh installation may claim administrator (review SEC-5). Default is on
// so the documented "sign up, then sign in" onboarding keeps working;
// exposed deployments set ALLOW_BOOTSTRAP_SIGNUP=0 and provision operators
// via ADMIN_EMAILS/seed instead, so an attacker scanning for new
// installations cannot claim the instance.
func AllowBootstrapSignup() bool {
return os.Getenv("ALLOW_BOOTSTRAP_SIGNUP") != "0"
}
+50
View File
@@ -0,0 +1,50 @@
package config
import "testing"
func TestCookieSecureDerivation(t *testing.T) {
t.Setenv("COOKIE_SECURE", "")
https := &Config{NextAuthURL: "https://app.example.com"}
if !https.CookieSecure() {
t.Fatal("https NEXTAUTH_URL must imply Secure cookies")
}
local := &Config{NextAuthURL: "http://localhost:3000"}
if local.CookieSecure() {
t.Fatal("http NEXTAUTH_URL must not set Secure")
}
if (&Config{}).CookieSecure() {
t.Fatal("unset NEXTAUTH_URL must not set Secure")
}
t.Setenv("COOKIE_SECURE", "1")
if !local.CookieSecure() {
t.Fatal("explicit COOKIE_SECURE=1 must win over http URL")
}
t.Setenv("COOKIE_SECURE", "0")
if https.CookieSecure() {
t.Fatal("explicit COOKIE_SECURE=0 must win over https URL")
}
}
func TestSecretPrecedence(t *testing.T) {
c := &Config{NextAuthSecret: "next", AuthSecret: "auth"}
if c.Secret() != "next" {
t.Fatal("NEXTAUTH_SECRET must win")
}
if (&Config{AuthSecret: "auth"}).Secret() != "auth" {
t.Fatal("AUTH_SECRET fallback broken")
}
}
// Review OPS-01: a dedicated ENCRYPTION_KEY decouples integration-credential
// encryption from the session signing secret.
func TestEncryptionSecretPrecedence(t *testing.T) {
c := &Config{AuthSecret: "auth"}
t.Setenv("ENCRYPTION_KEY", "")
if c.EncryptionSecret() != "auth" {
t.Fatal("without ENCRYPTION_KEY the auth secret must remain the key")
}
t.Setenv("ENCRYPTION_KEY", "dedicated-key")
if c.EncryptionSecret() != "dedicated-key" {
t.Fatal("ENCRYPTION_KEY must win over the auth secret")
}
}