fix: assign independent profiles to anonymous creator browsers
This commit is contained in:
@@ -1248,7 +1248,7 @@ func newAnonymousBrowser(ctx context.Context, store *hub.Store) (anonymousBrowse
|
||||
if template.Fingerprint.Seed < 1 {
|
||||
template.Fingerprint.Seed = time.Now().UnixNano()%2147483646 + 1
|
||||
}
|
||||
environment := hub.EnvironmentContext{Env: template, BindingVersion: 1}
|
||||
environment := hub.EnvironmentContext{Env: template, ProfileID: template.Alias, BindingVersion: 1}
|
||||
payload := gatewayCreatePayload(environment, "", gatewayNetworkExit{})
|
||||
status, body, callErr := gatewayCall(ctx, gateway, http.MethodPost, "/v1/browsers", payload, gatewayLongTimeout)
|
||||
if callErr == nil && status == http.StatusCreated {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
hub "git.ipao.vip/rogee/creator-hub/internal/environment"
|
||||
)
|
||||
|
||||
func TestAnonymousBrowserUsesIndependentProfile(t *testing.T) {
|
||||
databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL")
|
||||
if databaseURL == "" {
|
||||
t.Skip("set CREATORHUB_POSTGRES_TEST_URL to run PostgreSQL integration coverage")
|
||||
}
|
||||
for _, withTemplate := range []bool{false, true} {
|
||||
name := "without_template"
|
||||
if withTemplate {
|
||||
name = "with_template"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
store, err := hub.Open(ctx, isolatedControlPlaneDatabaseURL(t, databaseURL))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = store.Close() })
|
||||
|
||||
var payloads []struct {
|
||||
Alias string `json:"alias"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
}
|
||||
var purgedProfiles []string
|
||||
profilePattern := regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$`)
|
||||
gateway := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/v1/browsers":
|
||||
var payload struct {
|
||||
Alias string `json:"alias"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
t.Error(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
payloads = append(payloads, payload)
|
||||
if !profilePattern.MatchString(payload.ProfileID) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(`{"detail":"profile_id is invalid"}`))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"id": strings.Repeat("a", 64), "network_id": "native-" + strings.Repeat("b", 32),
|
||||
"alias": payload.Alias, "binding_version": 1, "state": "running",
|
||||
})
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/v1/browsers":
|
||||
_, _ = w.Write([]byte(`[]`))
|
||||
case r.Method == http.MethodDelete && strings.HasPrefix(r.URL.Path, "/v1/browsers/"):
|
||||
var payload struct {
|
||||
PurgeProfile bool `json:"purge_profile"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !payload.PurgeProfile {
|
||||
t.Error("anonymous cleanup must purge its temporary profile")
|
||||
}
|
||||
// The gateway purges the profile saved in the runtime selected by alias.
|
||||
alias := strings.TrimPrefix(r.URL.Path, "/v1/browsers/")
|
||||
for _, created := range payloads {
|
||||
if created.Alias == alias {
|
||||
purgedProfiles = append(purgedProfiles, created.ProfileID)
|
||||
break
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer gateway.Close()
|
||||
if _, err := store.CreateGateway(ctx, "gw-profile-test", gateway.URL, "unit-test-gateway-token"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var template hub.EnvironmentContext
|
||||
if withTemplate {
|
||||
template, err = store.CreateStandaloneEnv(ctx, "gw-profile-test", hub.Fingerprint{}, 4096)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
lease, err := newAnonymousBrowser(ctx, store)
|
||||
if err != nil {
|
||||
t.Fatalf("anonymous browser creation failed: %v", err)
|
||||
}
|
||||
if err := lease.close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
payload := payloads[i]
|
||||
if payload.ProfileID != payload.Alias || !strings.HasPrefix(payload.ProfileID, "anon-") {
|
||||
t.Fatalf("anonymous profile must use its own alias: %+v", payload)
|
||||
}
|
||||
if purgedProfiles[i] != payload.ProfileID {
|
||||
t.Fatalf("cleanup purged another profile: got %q want %q", purgedProfiles[i], payload.ProfileID)
|
||||
}
|
||||
if withTemplate && payload.ProfileID == template.ProfileID {
|
||||
t.Fatal("anonymous browser reused an existing environment profile")
|
||||
}
|
||||
}
|
||||
if payloads[0].ProfileID == payloads[1].ProfileID {
|
||||
t.Fatal("anonymous browsers must not share a profile")
|
||||
}
|
||||
if withTemplate {
|
||||
stored, err := store.GetEnvironmentContext(ctx, template.Alias)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stored.ProfileID != template.ProfileID || stored.Fingerprint.Seed != template.Fingerprint.Seed {
|
||||
t.Fatal("anonymous browser changed the existing environment identity")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user