mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
Add admin and master authentication layers with JWT support. Replace direct key creation with hierarchical master/child key system. Update database schema to support master accounts with configurable limits and epoch-based key revocation. Add health check endpoint with system status monitoring. BREAKING CHANGE: Removed direct POST /keys endpoint in favor of master-based key issuance through /v1/tokens. Database migration requires dropping old User table and creating Master table with new relationships.
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestEndToEnd(t *testing.T) {
|
|
apiBase := getenv("E2E_EZAPI_URL", "http://localhost:8080")
|
|
adminToken := getenv("EZ_ADMIN_TOKEN", "admin-token") // Make sure this matches docker-compose
|
|
|
|
client := &http.Client{Timeout: 5 * time.Second}
|
|
|
|
// 1. Admin creates a Master Key
|
|
masterPayload := map[string]interface{}{
|
|
"name": "test-master",
|
|
"group": "default",
|
|
"max_child_keys": 2,
|
|
"global_qps": 10,
|
|
}
|
|
var masterResp struct {
|
|
MasterKey string `json:"master_key"`
|
|
}
|
|
postJSONWithAuth(t, client, apiBase+"/admin/masters", masterPayload, &masterResp, adminToken)
|
|
masterKey := masterResp.MasterKey
|
|
require.NotEmpty(t, masterKey)
|
|
|
|
// 2. Master issues a Child Key
|
|
childPayload := map[string]interface{}{
|
|
"group": "default",
|
|
"scopes": "chat:write",
|
|
}
|
|
var childResp struct {
|
|
KeySecret string `json:"key_secret"`
|
|
}
|
|
postJSONWithAuth(t, client, apiBase+"/v1/tokens", childPayload, &childResp, masterKey)
|
|
childKey := childResp.KeySecret
|
|
require.NotEmpty(t, childKey)
|
|
|
|
// 3. (Conceptual) Use Child Key to access balancer - this part can't be fully tested here
|
|
// but we've verified the key generation flow.
|
|
t.Logf("Admin Token: %s", adminToken)
|
|
t.Logf("Master Key: %s", masterKey)
|
|
t.Logf("Child Key: %s", childKey)
|
|
}
|
|
|
|
func postJSONWithAuth[T any](t *testing.T, client *http.Client, url string, body interface{}, out T, token string) T {
|
|
b, err := json.Marshal(body)
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
|
|
require.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
if token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
|
data, _ := io.ReadAll(resp.Body)
|
|
t.Fatalf("expected 200 or 201, got %d. Body: %s", resp.StatusCode, string(data))
|
|
}
|
|
|
|
if out != nil {
|
|
data, _ := io.ReadAll(resp.Body)
|
|
if len(data) > 0 {
|
|
require.NoError(t, json.Unmarshal(data, out))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func getenv(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|