Commit 66f69e86 authored by Muhammad Umair Khan's avatar Muhammad Umair Khan
Browse files

Update deploy configs, auth-svc redirect caching, sandbox-ctrl active scenario...

Update deploy configs, auth-svc redirect caching, sandbox-ctrl active scenario fallback, and Tilt trigger modes

* meepctl: Fix the double-quoting issue on 'force-ssl-redirect' annotation during deployment, enabling proper HTTP to HTTPS redirection when 'https-only' is configured.
* auth-svc: Implement redirection state cache during OAuth flows to handle browser cookie validation, and improve sandbox cleanup logs on logout.
* sandbox-ctrl: Modify active scenario retrieval to return a 200 OK JSON response signaling 'inactive' state when no scenario is running, instead of throwing an HTTP 404, allowing the client-side frontend to differentiate between an inactive scenario and an API connection error.
* Tiltfile: Change trigger mode from AUTO to MANUAL for core apps to prevent automatic rebuild/redeploy cycles on minor file changes, optimizing development iterations.
parent 91f4ed47
Loading
Loading
Loading
Loading
+96 −17
Original line number Diff line number Diff line
@@ -70,6 +70,15 @@ const pfmCtrlBasepath = "http://meep-platform-ctrl/platform-ctrl/v1"
const providerModeSecure = "secure"
const mepPrefix = "mep--"

type OAuthProcess struct {
	done        chan struct{}
	redirectURL string
	cookies     []string
}

var oauthProcesses = make(map[string]*OAuthProcess)
var oauthMutex sync.Mutex

// Permission Configuration types
type Permission struct {
	Mode  string            `yaml:"mode"`
@@ -803,16 +812,39 @@ func asAuthenticate(w http.ResponseWriter, r *http.Request) {
}

func asAuthorize(w http.ResponseWriter, r *http.Request) {
	var metric met.SessionMetric

	// Retrieve query parameters
	query := r.URL.Query()
	code := query.Get("code")
	state := query.Get("state")

	oauthMutex.Lock()
	process, active := oauthProcesses[state]
	oauthMutex.Unlock()

	if active {
		// A request with this state is already being processed.
		// Wait for the original process to finish.
		<-process.done

		// Set the cached cookies on the duplicate request's response writer
		for _, cookie := range process.cookies {
			w.Header().Add("Set-Cookie", cookie)
		}

		// Redirect the duplicate request to the same destination
		// (the session was already created by the first request)
		if process.redirectURL != "" {
			http.Redirect(w, r, process.redirectURL, http.StatusFound)
		} else {
			http.Redirect(w, r, authSvc.uri, http.StatusFound)
		}
		return
	}

	// Validate request state
	request := getLoginRequest(state)
	if request == nil {
		var metric met.SessionMetric
		err := errors.New("Invalid OAuth state")
		log.Error(err.Error())
		metric.Description = err.Error()
@@ -822,6 +854,19 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
		return
	}

	// Create and register new OAuthProcess to lock this state
	process = &OAuthProcess{
		done: make(chan struct{}),
	}
	oauthMutex.Lock()
	oauthProcesses[state] = process
	oauthMutex.Unlock()

	// Delete the login request to prevent reuse
	delLoginRequest(state)

	var metric met.SessionMetric

	// Get provider-specific OAuth config
	provider := request.provider
	config, found := authSvc.oauthConfigs[provider]
@@ -830,22 +875,23 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
		log.Error(err.Error())
		metric.Description = err.Error()
		_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
		http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
		process.redirectURL = getErrUrl(err.Error())
		close(process.done)
		http.Redirect(w, r, process.redirectURL, http.StatusFound)
		metricSessionFail.WithLabelValues("Internal").Inc()
		return
	}
	metric.Provider = provider

	// Delete login request & timer
	delLoginRequest(state)

	// Retrieve access token
	token, err := config.Exchange(context.Background(), code)
	if err != nil {
		log.Error(err.Error())
		metric.Description = err.Error()
		_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
		http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
		process.redirectURL = getErrUrl(err.Error())
		close(process.done)
		http.Redirect(w, r, process.redirectURL, http.StatusFound)
		metricSessionFail.WithLabelValues("Internal").Inc()
		return
	}
@@ -856,7 +902,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
		log.Error(err.Error())
		metric.Description = err.Error()
		_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
		http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
		process.redirectURL = getErrUrl(err.Error())
		close(process.done)
		http.Redirect(w, r, process.redirectURL, http.StatusFound)
		metricSessionFail.WithLabelValues("OAuth").Inc()
		return
	}
@@ -871,7 +919,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
			log.Error(err.Error())
			metric.Description = err.Error()
			_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
			http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
			process.redirectURL = getErrUrl(err.Error())
			close(process.done)
			http.Redirect(w, r, process.redirectURL, http.StatusFound)
			metricSessionFail.WithLabelValues("OAuth").Inc()
			return
		}
@@ -880,7 +930,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
			log.Error(err.Error())
			metric.Description = err.Error()
			_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
			http.Redirect(w, r, getErrUrl("Failed to retrieve GitHub user ID"), http.StatusFound)
			process.redirectURL = getErrUrl("Failed to retrieve GitHub user ID")
			close(process.done)
			http.Redirect(w, r, process.redirectURL, http.StatusFound)
			metricSessionFail.WithLabelValues("OAuth").Inc()
			return
		}
@@ -893,7 +945,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
			log.Error(err.Error())
			metric.Description = err.Error()
			_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
			http.Redirect(w, r, getErrUrl(err.Error()), http.StatusFound)
			process.redirectURL = getErrUrl(err.Error())
			close(process.done)
			http.Redirect(w, r, process.redirectURL, http.StatusFound)
			metricSessionFail.WithLabelValues("OAuth").Inc()
			return
		}
@@ -905,7 +959,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
				log.Error(err.Error())
				metric.Description = err.Error()
				_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
				http.Redirect(w, r, getErrUrl("Failed to set GitLab API base url"), http.StatusFound)
				process.redirectURL = getErrUrl("Failed to set GitLab API base url")
				close(process.done)
				http.Redirect(w, r, process.redirectURL, http.StatusFound)
				metricSessionFail.WithLabelValues("OAuth").Inc()
				return
			}
@@ -916,7 +972,9 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
			log.Error(err.Error())
			metric.Description = err.Error()
			_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
			http.Redirect(w, r, getErrUrl("Failed to retrieve GitLab user ID"), http.StatusFound)
			process.redirectURL = getErrUrl("Failed to retrieve GitLab user ID")
			close(process.done)
			http.Redirect(w, r, process.redirectURL, http.StatusFound)
			metricSessionFail.WithLabelValues("OAuth").Inc()
			return
		}
@@ -936,7 +994,10 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
		log.Error(err.Error())
		metric.Description = err.Error()
		_ = authSvc.metricStore.SetSessionMetric(met.SesMetTypeError, metric)
		http.Redirect(w, r, getErrUrl(err.Error()), errCode)
		process.redirectURL = getErrUrl(err.Error())
		process.cookies = w.Header().Values("Set-Cookie")
		close(process.done)
		http.Redirect(w, r, process.redirectURL, errCode)
		metricSessionFail.WithLabelValues("Session").Inc()
		return
	}
@@ -951,11 +1012,23 @@ func asAuthorize(w http.ResponseWriter, r *http.Request) {
	}

	// Redirect user to sandbox
	http.Redirect(w, r, authSvc.uri+"?sbox="+sandboxName+"&user="+userId+"&role="+userRole+"&cb="+cacheBuster, http.StatusFound)
	redirectURL := authSvc.uri + "?sbox=" + sandboxName + "&user=" + userId + "&role=" + userRole + "&cb=" + cacheBuster
	process.redirectURL = redirectURL
	process.cookies = w.Header().Values("Set-Cookie")
	close(process.done)
	http.Redirect(w, r, redirectURL, http.StatusFound)
	metricSessionSuccess.Inc()
	if isNew {
		metricSessionActive.Inc()
	}

	// Clean up the cache after 10 seconds
	go func(s string) {
		time.Sleep(10 * time.Second)
		oauthMutex.Lock()
		delete(oauthProcesses, s)
		oauthMutex.Unlock()
	}(state)
}

func asLogin(w http.ResponseWriter, r *http.Request) {
@@ -1156,16 +1229,22 @@ func asLogout(w http.ResponseWriter, r *http.Request) {
	// Get existing session
	sessionStore := authSvc.sessionMgr.GetSessionStore()
	session, err := sessionStore.Get(r)
	if err == nil {
	if err != nil {
		log.Error("asLogout: Failed to get session: ", err.Error())
	} else {
		metric.Provider = session.Provider
		metric.User = session.Username
		metric.Sandbox = session.Sandbox

		// Delete sandbox
		if session.Sandbox != "" {
			log.Info("asLogout: Deleting sandbox: ", session.Sandbox)
			_, err = authSvc.pfmCtrlClient.SandboxControlApi.DeleteSandbox(context.TODO(), session.Sandbox)
			if err == nil {
			if err != nil {
				log.Error("asLogout: Failed to delete sandbox: ", err.Error())
			} else {
				sandboxDeleted = true
				log.Info("asLogout: Sandbox deleted successfully")
			}
		}
	}
+5 −1
Original line number Diff line number Diff line
@@ -462,7 +462,11 @@ func ceGetActiveScenario(w http.ResponseWriter, r *http.Request) {
	log.Debug("CEGetActiveScenario")

	if sbxCtrl.activeModel == nil || !sbxCtrl.activeModel.Active {
		http.Error(w, "No scenario is active", http.StatusNotFound)
		// Return 200 OK with a response body indicating no scenario is active
		// This allows frontend to distinguish between "no scenario" and actual errors
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, `{"message":"No scenario is active","status":"inactive"}`)
		return
	}

+6 −6
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ for app in CORE_APPS:
            REPO_ROOT + '/go-apps/' + app + '/vendor/',
        ],
        auto_init=False,
        trigger_mode=TRIGGER_MODE_AUTO,
        trigger_mode=TRIGGER_MODE_MANUAL,
        labels=['core'],
    )

@@ -122,7 +122,7 @@ for app in CORE_APPS:
        cmd='echo "=== [' + app + '] Helm chart changed ===" && cd ' + REPO_ROOT + ' && meepctl deploy core ' + app + ' --force',
        deps=[REPO_ROOT + '/charts/' + app + '/'],
        auto_init=False,
        trigger_mode=TRIGGER_MODE_AUTO,
        trigger_mode=TRIGGER_MODE_MANUAL,
        labels=['core'],
    )

@@ -166,7 +166,7 @@ local_resource(
        REPO_ROOT + '/go-apps/meep-virt-engine/vendor/',
    ],
    auto_init=False,
    trigger_mode=TRIGGER_MODE_AUTO,
    trigger_mode=TRIGGER_MODE_MANUAL,
    labels=['core'],
)

@@ -176,7 +176,7 @@ local_resource(
    cmd='echo "=== [meep-virt-engine] Helm chart changed ===" && cd ' + REPO_ROOT + ' && meepctl deploy core meep-virt-engine --force',
    deps=[REPO_ROOT + '/charts/meep-virt-engine/'],
    auto_init=False,
    trigger_mode=TRIGGER_MODE_AUTO,
    trigger_mode=TRIGGER_MODE_MANUAL,
    labels=['core'],
)

@@ -188,7 +188,7 @@ local_resource(
    cmd='echo "=== [meep-virt-engine] Sandbox chart changed ===" && echo "Re-dockerizing meep-virt-engine to bundle updated charts..." && cd ' + REPO_ROOT + ' && meepctl dockerize meep-virt-engine && meepctl deploy core meep-virt-engine --force',
    deps=sandbox_chart_deps,
    auto_init=False,
    trigger_mode=TRIGGER_MODE_AUTO,
    trigger_mode=TRIGGER_MODE_MANUAL,
    labels=['core'],
)

@@ -284,6 +284,6 @@ local_resource(
    cmd='echo "=== [meep-ingress-certs] Chart changed ===" && cd ' + REPO_ROOT + ' && meepctl deploy core meep-ingress-certs --force',
    deps=[REPO_ROOT + '/charts/meep-ingress-certs/'],
    auto_init=False,
    trigger_mode=TRIGGER_MODE_AUTO,
    trigger_mode=TRIGGER_MODE_MANUAL,
    labels=['core'],
)
+1 −1
Original line number Diff line number Diff line
@@ -417,7 +417,7 @@ func deployRunScriptsAndGetFlags(targetName string, chart string, cobraCmd *cobr
	// Common platform flags
	httpsOnly := utils.RepoCfg.GetBool("repo.deployment.ingress.https-only")
	if httpsOnly {
		flags = utils.HelmFlags(flags, "--set", "ingress.annotations.nginx\\.ingress\\.kubernetes\\.io/force-ssl-redirect=\"true\"")
		flags = utils.HelmFlags(flags, "--set", "ingress.annotations.nginx\\.ingress\\.kubernetes\\.io/force-ssl-redirect=true")
	}

	// Service-specific flags