tablo_core/panel/
impersonation.rs1use std::convert::Infallible;
5
6use topcoat::{
7 context::Cx,
8 icon::{IconData, icon},
9 view::{BoxView, ViewExt, attributes, view},
10};
11
12use crate::{Action, Outcome, PanelUser, Places, Resource, WriteError};
13
14#[derive(Debug, Clone, Copy)]
36pub struct Impersonate;
37
38impl<R: Resource> Action<R> for Impersonate
39where
40 R::Model: PanelUser,
41{
42 type Input = ();
43
44 const NAME: &'static str = "impersonate";
45 const PLACES: Places = Places::RECORD;
46 const ICON: Option<IconData> = Some(tablo_ui::icons::EYE);
47
48 fn label() -> crate::Text {
49 crate::text!("tablo-impersonation-start")
50 }
51
52 fn can_run(cx: &Cx, record: &R::Model) -> bool {
53 crate::auth::may_impersonate(cx, record)
54 }
55
56 async fn run(
57 cx: &Cx,
58 records: &[R::Model],
59 (): (),
60 ex: &mut dyn toasty::Executor,
61 ) -> Result<Outcome, WriteError<Infallible>> {
62 let [target] = records else {
63 return Err(WriteError::declined(crate::text!(
64 "tablo-impersonation-one"
65 )));
66 };
67 crate::auth::impersonate(cx, ex, target)
68 .await
69 .map_err(WriteError::Failed)?;
70 Ok(Outcome::done().redirect(crate::auth::panel_prefix(cx)))
71 }
72
73 fn success(_count: usize) -> crate::Text {
74 crate::text!("tablo-impersonation-started")
75 }
76}
77
78pub(super) fn banner(cx: &Cx) -> BoxView<'_> {
81 match crate::auth::signed(cx) {
82 Some(signed) if signed.impersonator.is_some() => strip(cx, signed),
83 _ => view! {}.boxed(),
84 }
85}
86
87fn strip<'a>(cx: &'a Cx, signed: &'a crate::auth::SignedIn) -> BoxView<'a> {
88 let csrf = crate::csrf::ensure_token(cx);
89 let stop = crate::auth::stop_impersonating_url(cx);
90 let name = signed.user.display_name().to_owned();
91 view! {
92 <div
93 class="flex flex-wrap items-center justify-between gap-2 border-b border-warning/40 \
94 bg-warning/15 px-4 py-2 text-sm text-foreground"
95 role="status"
96 data-impersonating=(signed.user.user_id())
97 >
98 <span>(crate::t!(cx, "tablo-impersonation-banner", name = name))</span>
99 <form method="post" action=(stop)>
100 (crate::csrf::field(cx, &csrf))
101 tablo_ui::button(
102 variant: tablo_ui::ButtonVariant::Outline,
103 size: tablo_ui::ButtonSize::Sm,
104 attrs: attributes! { type="submit" },
105 icon(data: tablo_ui::icons::LOG_OUT)
106 (crate::t!(cx, "tablo-impersonation-stop"))
107 )
108 </form>
109 </div>
110 }
111 .boxed()
112}