From 6df48c1413a14516e7ee8919f33fbc13f0141966 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Sat, 1 Aug 2026 02:39:59 +0200 Subject: Show and extend the elevation window A banner appears while elevated, counting down in minutes and carrying extend and drop controls. Three extensions of 30 minutes are allowed, so the window is at most two hours without a fresh code; a code resets the budget. The countdown is advisory, the server-side check authoritative, so it says "expired" rather than vanishing. The post-login flash tells an admin the timer has started. --- app/controllers/elevations_controller.rb | 11 +++++++ app/controllers/otp_challenges_controller.rb | 7 ++++- app/views/layouts/_elevation_banner.html.erb | 47 ++++++++++++++++++++++++++++ app/views/layouts/admin.html.erb | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 app/views/layouts/_elevation_banner.html.erb (limited to 'app') diff --git a/app/controllers/elevations_controller.rb b/app/controllers/elevations_controller.rb index 804b8f95..229f33d8 100644 --- a/app/controllers/elevations_controller.rb +++ b/app/controllers/elevations_controller.rb @@ -41,4 +41,15 @@ class ElevationsController < ApplicationController flash[:notice] = t("flash.elevation.dropped") redirect_to admin_path end + + def renew + if renew_elevation! + flash[:notice] = t("flash.elevation.renewed", + :minutes => AuthenticatedSystem::ELEVATION_MAX_AGE.in_minutes.to_i, + :left => elevation_extensions_left) + else + flash[:error] = t("flash.elevation.cannot_renew") + end + redirect_back(:fallback_location => admin_path, :allow_other_host => false) + end end diff --git a/app/controllers/otp_challenges_controller.rb b/app/controllers/otp_challenges_controller.rb index 87586241..2526d1fb 100644 --- a/app/controllers/otp_challenges_controller.rb +++ b/app/controllers/otp_challenges_controller.rb @@ -31,7 +31,12 @@ class OtpChallengesController < ApplicationController # an admin who logs in and goes straight to user management # is already elevated elevate! if user.is_admin? - flash[:notice] = t("flash.common.logged_in") + flash[:notice] = if user.is_admin? + t("flash.elevation.granted_at_login", + :minutes => AuthenticatedSystem::ELEVATION_MAX_AGE.in_minutes.to_i) + else + t("flash.common.logged_in") + end redirect_to safe_return_to(return_to, :default => admin_path) else flash.now[:error] = t("flash.otp.code_mismatch") diff --git a/app/views/layouts/_elevation_banner.html.erb b/app/views/layouts/_elevation_banner.html.erb new file mode 100644 index 00000000..a7d15a2e --- /dev/null +++ b/app/views/layouts/_elevation_banner.html.erb @@ -0,0 +1,47 @@ +<% if elevated? %> +
" + data-label-less-than-a-minute="<%= t(".less_than_a_minute") %>" + data-label-expired="<%= t(".expired") %>"> + + <%= icon("shield-check", library: "tabler", "aria-hidden": true) %> + <%= t(".active") %> + + + + <% if elevation_extensions_left > 0 %> + <%= button_to t(".renew"), renew_elevation_path, method: :post, + form: { class: "button_to state_changing" } %> + <% end %> + <%= button_to t(".drop"), elevation_path, method: :delete, + form: { class: "button_to destructive" } %> + +
+ + <%= javascript_tag nonce: true do %> + (function () { + var banner = document.getElementById("elevation_banner"); + if (!banner) return; + var out = document.getElementById("elevation_remaining"); + var expires = parseInt(banner.dataset.expiresAt, 10) * 1000; + + function tick() { + var ms = expires - Date.now(); + if (ms <= 0) { + out.textContent = banner.dataset.labelExpired; + banner.classList.remove("elevation_soon"); + banner.classList.add("elevation_expired"); + return; + } + var mins = Math.floor(ms / 60000); + out.textContent = mins < 1 + ? banner.dataset.labelLessThanAMinute + : banner.dataset.labelMinutes.replace("%{count}", mins); + if (mins < 5) banner.classList.add("elevation_soon"); + setTimeout(tick, 15000); + } + tick(); + })(); + <% end %> +<% end %> diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb index 43f09b96..a84c8927 100644 --- a/app/views/layouts/admin.html.erb +++ b/app/views/layouts/admin.html.erb @@ -43,6 +43,7 @@
+ <%= render "layouts/elevation_banner" %> <%= render "layouts/flash" %>
<%= yield %> -- cgit v1.3