portal: enforce onboarding step prerequisites
This commit is contained in:
parent
5f8ef42d79
commit
a63bb3b048
@ -65,6 +65,16 @@ ONBOARDING_STEPS: tuple[str, ...] = (
|
|||||||
|
|
||||||
KEYCLOAK_MANAGED_STEPS: set[str] = {"keycloak_password_changed"}
|
KEYCLOAK_MANAGED_STEPS: set[str] = {"keycloak_password_changed"}
|
||||||
|
|
||||||
|
ONBOARDING_STEP_PREREQUISITES: dict[str, set[str]] = {
|
||||||
|
"vaultwarden_master_password": {"keycloak_password_changed"},
|
||||||
|
"element_recovery_key": {"keycloak_password_changed", "vaultwarden_master_password"},
|
||||||
|
"element_recovery_key_stored": {
|
||||||
|
"keycloak_password_changed",
|
||||||
|
"vaultwarden_master_password",
|
||||||
|
"element_recovery_key",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _normalize_status(status: str) -> str:
|
def _normalize_status(status: str) -> str:
|
||||||
cleaned = (status or "").strip().lower()
|
cleaned = (status or "").strip().lower()
|
||||||
@ -562,6 +572,12 @@ def register(app) -> None:
|
|||||||
mark_done = completed
|
mark_done = completed
|
||||||
|
|
||||||
if mark_done:
|
if mark_done:
|
||||||
|
prerequisites = ONBOARDING_STEP_PREREQUISITES.get(step, set())
|
||||||
|
if prerequisites:
|
||||||
|
current_completed = _completed_onboarding_steps(conn, code, username)
|
||||||
|
missing = sorted(prerequisites - current_completed)
|
||||||
|
if missing:
|
||||||
|
return jsonify({"error": "step is blocked", "blocked_by": missing}), 409
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO access_request_onboarding_steps (request_code, step)
|
INSERT INTO access_request_onboarding_steps (request_code, step)
|
||||||
|
|||||||
@ -140,10 +140,13 @@
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="isStepDone('vaultwarden_master_password')"
|
:checked="isStepDone('vaultwarden_master_password')"
|
||||||
:disabled="!auth.authenticated || loading"
|
:disabled="!auth.authenticated || loading || isStepBlocked('vaultwarden_master_password')"
|
||||||
@change="toggleStep('vaultwarden_master_password', $event)"
|
@change="toggleStep('vaultwarden_master_password', $event)"
|
||||||
/>
|
/>
|
||||||
<span>Set a Vaultwarden master password</span>
|
<span>Set a Vaultwarden master password</span>
|
||||||
|
<span class="pill mono auto-pill" :class="stepPillClass('vaultwarden_master_password')">
|
||||||
|
{{ stepPillLabel("vaultwarden_master_password") }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<p class="muted">
|
<p class="muted">
|
||||||
Open <a href="https://vault.bstein.dev" target="_blank" rel="noreferrer">Passwords</a> and set a strong master
|
Open <a href="https://vault.bstein.dev" target="_blank" rel="noreferrer">Passwords</a> and set a strong master
|
||||||
@ -156,10 +159,13 @@
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="isStepDone('element_recovery_key')"
|
:checked="isStepDone('element_recovery_key')"
|
||||||
:disabled="!auth.authenticated || loading"
|
:disabled="!auth.authenticated || loading || isStepBlocked('element_recovery_key')"
|
||||||
@change="toggleStep('element_recovery_key', $event)"
|
@change="toggleStep('element_recovery_key', $event)"
|
||||||
/>
|
/>
|
||||||
<span>Create an Element recovery key</span>
|
<span>Create an Element recovery key</span>
|
||||||
|
<span class="pill mono auto-pill" :class="stepPillClass('element_recovery_key')">
|
||||||
|
{{ stepPillLabel("element_recovery_key") }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<p class="muted">
|
<p class="muted">
|
||||||
In Element, create a recovery key so you can restore encrypted history if you lose a device.
|
In Element, create a recovery key so you can restore encrypted history if you lose a device.
|
||||||
@ -171,10 +177,13 @@
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="isStepDone('element_recovery_key_stored')"
|
:checked="isStepDone('element_recovery_key_stored')"
|
||||||
:disabled="!auth.authenticated || loading"
|
:disabled="!auth.authenticated || loading || isStepBlocked('element_recovery_key_stored')"
|
||||||
@change="toggleStep('element_recovery_key_stored', $event)"
|
@change="toggleStep('element_recovery_key_stored', $event)"
|
||||||
/>
|
/>
|
||||||
<span>Store the recovery key in Vaultwarden</span>
|
<span>Store the recovery key in Vaultwarden</span>
|
||||||
|
<span class="pill mono auto-pill" :class="stepPillClass('element_recovery_key_stored')">
|
||||||
|
{{ stepPillLabel("element_recovery_key_stored") }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<p class="muted">Save the recovery key in Vaultwarden so it doesn't get lost.</p>
|
<p class="muted">Save the recovery key in Vaultwarden so it doesn't get lost.</p>
|
||||||
</li>
|
</li>
|
||||||
@ -247,6 +256,33 @@ function isStepDone(step) {
|
|||||||
return Array.isArray(steps) ? steps.includes(step) : false;
|
return Array.isArray(steps) ? steps.includes(step) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isStepBlocked(step) {
|
||||||
|
const order = [
|
||||||
|
"keycloak_password_changed",
|
||||||
|
"vaultwarden_master_password",
|
||||||
|
"element_recovery_key",
|
||||||
|
"element_recovery_key_stored",
|
||||||
|
];
|
||||||
|
const idx = order.indexOf(step);
|
||||||
|
if (idx <= 0) return false;
|
||||||
|
for (let i = 0; i < idx; i += 1) {
|
||||||
|
if (!isStepDone(order[i])) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stepPillLabel(step) {
|
||||||
|
if (isStepDone(step)) return "done";
|
||||||
|
if (isStepBlocked(step)) return "blocked";
|
||||||
|
return "pending";
|
||||||
|
}
|
||||||
|
|
||||||
|
function stepPillClass(step) {
|
||||||
|
if (isStepDone(step)) return "pill-ok";
|
||||||
|
if (isStepBlocked(step)) return "pill-wait";
|
||||||
|
return "pill-warn";
|
||||||
|
}
|
||||||
|
|
||||||
function taskPillClass(status) {
|
function taskPillClass(status) {
|
||||||
const key = (status || "").trim();
|
const key = (status || "").trim();
|
||||||
if (key === "ok") return "pill-ok";
|
if (key === "ok") return "pill-ok";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user