mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 17:19:57 +00:00
145 lines
4.7 KiB
Go
145 lines
4.7 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
internalauth "github.com/rcourtman/pulse-go-rewrite/pkg/auth"
|
|
)
|
|
|
|
func newHostedAuthzTestRouter(t *testing.T) *Router {
|
|
baseDir := t.TempDir()
|
|
|
|
hashed, err := internalauth.HashPassword("Password!1")
|
|
if err != nil {
|
|
t.Fatalf("hash password: %v", err)
|
|
}
|
|
|
|
mtp := config.NewMultiTenantPersistence(baseDir)
|
|
if err := mtp.SaveOrganization(&models.Organization{
|
|
ID: "acme",
|
|
DisplayName: "Acme",
|
|
OwnerUserID: "alice",
|
|
Members: []models.OrganizationMember{
|
|
{UserID: "alice", Role: models.OrgRoleOwner, AddedAt: time.Now()},
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("seed org acme: %v", err)
|
|
}
|
|
if err := mtp.SaveOrganization(&models.Organization{
|
|
ID: "beta",
|
|
DisplayName: "Beta",
|
|
OwnerUserID: "bob",
|
|
Members: []models.OrganizationMember{
|
|
{UserID: "bob", Role: models.OrgRoleOwner, AddedAt: time.Now()},
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("seed org beta: %v", err)
|
|
}
|
|
|
|
router := &Router{
|
|
mux: http.NewServeMux(),
|
|
config: &config.Config{DataPath: baseDir, AuthUser: "admin", AuthPass: hashed},
|
|
multiTenant: mtp,
|
|
hostedMode: true,
|
|
}
|
|
router.registerHostedRoutes(nil, nil, nil)
|
|
t.Cleanup(func() {
|
|
if router.signupRateLimiter != nil {
|
|
router.signupRateLimiter.Stop()
|
|
}
|
|
})
|
|
return router
|
|
}
|
|
|
|
func newHostedAuthzNonOwnerRequest(method, path, body string) *http.Request {
|
|
req := httptest.NewRequest(method, path, strings.NewReader(body))
|
|
req.AddCookie(&http.Cookie{Name: "pulse_session", Value: "billing-authz-alice-session"})
|
|
// Ensure basic auth isn't accidentally used (would be platform admin).
|
|
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:wrong")))
|
|
return req
|
|
}
|
|
|
|
func TestBillingStateAdminEndpoint_NonOwnerGets403(t *testing.T) {
|
|
router := newHostedAuthzTestRouter(t)
|
|
|
|
// Authenticate as alice (session user), but attempt to access bob's org.
|
|
sessionToken := "billing-authz-alice-session"
|
|
GetSessionStore().CreateSession(sessionToken, time.Hour, "agent", "127.0.0.1", "alice")
|
|
|
|
req := newHostedAuthzNonOwnerRequest(http.MethodGet, "/api/admin/orgs/beta/billing-state", "")
|
|
|
|
rec := httptest.NewRecorder()
|
|
router.mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("expected 403 for non-owner, got %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHostedLifecycleSuspend_NonOwnerGets403(t *testing.T) {
|
|
router := newHostedAuthzTestRouter(t)
|
|
|
|
sessionToken := "billing-authz-alice-session"
|
|
GetSessionStore().CreateSession(sessionToken, time.Hour, "agent", "127.0.0.1", "alice")
|
|
|
|
req := newHostedAuthzNonOwnerRequest(http.MethodPost, "/api/admin/orgs/beta/suspend", `{"reason":"test"}`)
|
|
rec := httptest.NewRecorder()
|
|
router.mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("expected 403 for non-owner suspend attempt, got %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHostedAgentInstallCommand_NonOwnerGets403(t *testing.T) {
|
|
router := newHostedAuthzTestRouter(t)
|
|
|
|
sessionToken := "billing-authz-alice-session"
|
|
GetSessionStore().CreateSession(sessionToken, time.Hour, "agent", "127.0.0.1", "alice")
|
|
|
|
req := newHostedAuthzNonOwnerRequest(http.MethodPost, "/api/admin/orgs/beta/agent-install-command", `{"type":"pve"}`)
|
|
rec := httptest.NewRecorder()
|
|
router.mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("expected 403 for non-owner agent install command attempt, got %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHostedOrganizationsList_SessionUserGets403(t *testing.T) {
|
|
router := newHostedAuthzTestRouter(t)
|
|
|
|
sessionToken := "billing-authz-alice-session"
|
|
GetSessionStore().CreateSession(sessionToken, time.Hour, "agent", "127.0.0.1", "alice")
|
|
|
|
req := newHostedAuthzNonOwnerRequest(http.MethodGet, "/api/hosted/organizations", "")
|
|
rec := httptest.NewRecorder()
|
|
router.mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("expected 403 for non-platform session on hosted organizations, got %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHostedOrganizationsList_ConfiguredAdminSessionGets200(t *testing.T) {
|
|
router := newHostedAuthzTestRouter(t)
|
|
|
|
sessionToken := "billing-authz-admin-session"
|
|
GetSessionStore().CreateSession(sessionToken, time.Hour, "agent", "127.0.0.1", "admin")
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/hosted/organizations", nil)
|
|
req.AddCookie(&http.Cookie{Name: "pulse_session", Value: sessionToken})
|
|
rec := httptest.NewRecorder()
|
|
router.mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 for configured admin session, got %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|