package app import ( "context" "errors" "net/http" "net/http/httptest" "testing" "agent-system-backend/internal/settings" "agent-system-backend/internal/store" ) func TestInprocHostRechecksEffectiveAccountOnEveryCapability(t *testing.T) { st, err := store.OpenSQLite(t.TempDir() + "/host.db") if err != nil { t.Fatal(err) } defer st.Close() if err := st.CreateAccount("alice", "", "", false); err != nil { t.Fatal(err) } check := accountEffectiveAuthorization(st, true) h := inprocHost{caps: hostcaps{st: st, accountGuard: func(owner string) error { valid, err := check(context.Background(), owner) if err != nil { return err } if !valid { return store.ErrAccountForbidden } return nil }}, owner: "alice", actor: "alice", pluginID: "rdm"} if err := h.KVPut("before", "allowed"); err != nil { t.Fatal(err) } if _, err := st.DB().Exec(`UPDATE users SET enabled=FALSE WHERE owner=?`, "alice"); err != nil { t.Fatal(err) } if err := h.KVPut("after", "denied"); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host wrote KV: %v", err) } if _, _, err := h.KVGet("before"); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host read KV: %v", err) } if _, err := h.TaskCreate("test", "denied", ""); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host created task: %v", err) } if _, err := h.ProjectNameAvailable("denied", ""); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host used project capability: %v", err) } if err := h.Audit("action", "target", ""); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host wrote audit: %v", err) } if _, err := h.Complete(context.Background(), "denied"); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host reached model completion: %v", err) } if _, err := h.KnowledgeSearch(context.Background(), "", "denied", 1); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("disabled host read knowledge: %v", err) } } func TestHostCompletionChecksAgainAfterModelSettingsRead(t *testing.T) { st, err := store.OpenSQLite(t.TempDir() + "/host-complete.db") if err != nil { t.Fatal(err) } defer st.Close() requests := 0 model := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { requests++ _, _ = w.Write([]byte(`{"choices":[{"message":{"content":"late"}}]}`)) })) defer model.Close() set := settings.NewService(st) set.SetSystemOverride(map[string]any{"models": map[string]any{"chat": map[string]any{"model": "test", "baseUrl": model.URL}}}) checks := 0 h := inprocHost{caps: hostcaps{st: st, set: set, accountGuard: func(string) error { checks++ if checks >= 3 { return store.ErrAccountForbidden } return nil }}, owner: "alice", actor: "alice", pluginID: "rdm"} if _, err := h.Complete(context.Background(), "late"); !errors.Is(err, store.ErrAccountForbidden) { t.Fatalf("completion after disable: %v", err) } if checks != 3 || requests != 0 { t.Fatalf("late completion reached model: checks=%d requests=%d", checks, requests) } } func TestImplicitLocalAccountExceptionIsAuthOffOnly(t *testing.T) { st, err := store.OpenSQLite(t.TempDir() + "/local.db") if err != nil { t.Fatal(err) } defer st.Close() allowed, err := accountEffectiveAuthorization(st, false)(context.Background(), store.OwnerDefault) if err != nil || !allowed { t.Fatalf("implicit local denied: %v %v", allowed, err) } allowed, err = accountEffectiveAuthorization(st, true)(context.Background(), store.OwnerDefault) if !errors.Is(err, store.ErrNotFound) || allowed { t.Fatalf("auth-on missing account allowed: %v %v", allowed, err) } if err := st.EnsureUser(store.OwnerDefault, ""); err != nil { t.Fatal(err) } if _, err := st.DB().Exec(`UPDATE users SET enabled=FALSE WHERE owner=?`, store.OwnerDefault); err != nil { t.Fatal(err) } allowed, err = accountEffectiveAuthorization(st, false)(context.Background(), store.OwnerDefault) if err != nil || allowed { t.Fatalf("disabled existing local account allowed: %v %v", allowed, err) } }