mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 00:01:49 +00:00
fix: the guest plan gate fails closed on non-enterprise builds; settings report the effective switch
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
//! is written (git-sync and the CLI push policies past every UI).
|
||||
//!
|
||||
//! The token is inserted directly: how a guest session is minted is the identity
|
||||
//! provider's business (EE), what one can do is this file's.
|
||||
//! provider's business (EE), what one can do is this file's. Build with
|
||||
//! `enterprise,private`, as CI does: the plan gate refuses every guest on a build
|
||||
//! without them, so on a plain CE build every test here fails at the door.
|
||||
//!
|
||||
//! Users from the `base` fixture:
|
||||
//! test-user (admin, token SECRET_TOKEN)
|
||||
|
||||
@@ -1099,6 +1099,10 @@ async fn get_settings(
|
||||
if let Some(git_sync) = settings.git_sync.as_mut() {
|
||||
redact_git_sync_webhook_secrets(git_sync);
|
||||
}
|
||||
// The effective value, not the column: a switch left on by a plan that no longer
|
||||
// admits guests must not read as open when every gate says shut.
|
||||
settings.guest_access_enabled =
|
||||
settings.guest_access_enabled && windmill_common::workspaces::guest_access_licensed().await;
|
||||
|
||||
Ok(Json(settings))
|
||||
}
|
||||
@@ -1135,8 +1139,10 @@ async fn get_public_settings(
|
||||
.await
|
||||
.map_err(|e| Error::internal_err(format!("getting public settings: {e:#}")))?;
|
||||
|
||||
let settings = not_found_if_none(settings, "workspace settings", &w_id)?;
|
||||
let mut settings = not_found_if_none(settings, "workspace settings", &w_id)?;
|
||||
tx.commit().await?;
|
||||
settings.guest_access_enabled =
|
||||
settings.guest_access_enabled && windmill_common::workspaces::guest_access_licensed().await;
|
||||
|
||||
Ok(Json(settings))
|
||||
}
|
||||
|
||||
@@ -775,10 +775,10 @@ pub struct BillableSeats {
|
||||
pub seats: i64,
|
||||
}
|
||||
|
||||
/// Guests are an Enterprise-plan feature, so a Pro key refuses them at every gate —
|
||||
/// the workspace switch, the mint, the door — not only in the UI that hides them. A
|
||||
/// build without `enterprise` has no plan to consult and cannot mint a guest session
|
||||
/// at all (the mint lives in EE code), so nothing is gated there.
|
||||
/// Guests are an Enterprise-plan feature, refused on any other plan and on any build
|
||||
/// without `enterprise` at every gate: the switch write, discovery, the mint, the
|
||||
/// door. The CE image compiles the OAuth callback that mints, so the build check is
|
||||
/// load-bearing, not a formality.
|
||||
pub async fn guest_access_licensed() -> bool {
|
||||
#[cfg(feature = "enterprise")]
|
||||
{
|
||||
@@ -789,12 +789,13 @@ pub async fn guest_access_licensed() -> bool {
|
||||
}
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
{
|
||||
true
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `w_id` admits guest sessions — someone the identity provider authenticated
|
||||
/// who is a member of no workspace, and who therefore takes no seat.
|
||||
/// who is a member of no workspace, and who therefore takes no seat. The plan is
|
||||
/// checked first: a switch left on by a plan that no longer admits guests is shut.
|
||||
///
|
||||
/// Read uncached where a session is minted ([`guest_app_admits`]) and then once per
|
||||
/// request at the auth door (`AuthCache::get_opt_job_authed`) for every guest. An app
|
||||
@@ -815,7 +816,7 @@ pub async fn is_guest_access_enabled(db: &crate::DB, w_id: &str) -> Result<bool>
|
||||
.unwrap_or(false))
|
||||
}
|
||||
|
||||
/// Both gates at once: the workspace admits guests and `app_path` is in `guest`
|
||||
/// Every gate at once: the plan, the workspace switch, and `app_path` being in `guest`
|
||||
/// execution mode. The single answer to "may a guest session be minted for this app",
|
||||
/// used by the mint itself and by the sign-in branch that decides whether to call it.
|
||||
/// A missing app or a policy with no stated mode reads as "no".
|
||||
|
||||
@@ -215,10 +215,9 @@
|
||||
let accountSession = $state<'unknown' | 'none' | 'held'>('unknown')
|
||||
let deniedStatus: number | undefined = $state(undefined)
|
||||
/** The sign-in card belongs on a 401, and on a 403 unless discovery has settled
|
||||
* that the app is not open to guests — a 403 on a guest app is a session for a
|
||||
* different app of the same workspace, which a fresh sign-in replaces. While
|
||||
* discovery is still pending this stays true so the skeleton shows rather than
|
||||
* a flash of "Not found". */
|
||||
* that the app is not open to guests: a 403 on a guest app is a session for another
|
||||
* app of the workspace, which a fresh sign-in replaces. True while discovery is
|
||||
* pending, so the skeleton shows rather than a flash of "Not found". */
|
||||
let offerSignIn = $derived(
|
||||
status === 'noPermission' ||
|
||||
(status === 'notExists' && deniedStatus === 403 && guestEntry !== 'none')
|
||||
@@ -235,10 +234,9 @@
|
||||
})
|
||||
|
||||
// The stale guest session must be gone before the card mounts: it still
|
||||
// authenticates in this workspace, so the popup's success poll would see it and
|
||||
// complete the sign-in before the new session ever lands. The card waits on
|
||||
// `staleGuestCleared`, and a failed logout fails closed rather than offering a
|
||||
// sign-in that could not complete.
|
||||
// authenticates here, so the popup's success poll would see it and complete the
|
||||
// sign-in before the new session lands. The card waits on `staleGuestCleared`; a
|
||||
// failed logout fails closed rather than offering a sign-in that cannot complete.
|
||||
let staleGuestCleared = $state(false)
|
||||
let staleGuestLogoutFailed = $state(false)
|
||||
$effect(() => {
|
||||
|
||||
Reference in New Issue
Block a user