1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! This is the top level router of the web ui for kanidm. It decides based on the incoming
//! request, where to direct this too, and if the requirements for that request have been
//! met before rendering. For example, if you land here with an oauth request, but you are
//! not authenticated, this will determine that and send you to authentication first, then
//! will allow you to proceed with the oauth flow.

use gloo::console;
use serde::{Deserialize, Serialize};
use wasm_bindgen::UnwrapThrowExt;
use yew::functional::*;
use yew::prelude::*;
use yew_router::prelude::*;

use crate::credential::reset::CredentialResetApp;
use crate::login::{LoginApp, LoginWorkflow};
use crate::oauth2::Oauth2App;
use crate::views::{ViewRoute, ViewsApp};

// router to decide on state.
#[derive(Routable, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub enum Route {
    #[at("/")]
    Landing,

    #[at("/ui/view/*")]
    Views,

    #[at("/ui/login")]
    Login,

    #[at("/ui/reauth")]
    Reauth,

    #[at("/ui/oauth2")]
    Oauth2,

    #[at("/ui/reset")]
    CredentialReset,

    #[not_found]
    #[at("/ui/404")]
    NotFound,
}

#[function_component(Landing)]
fn landing() -> Html {
    // Do this to allow use_navigator to work because lol.
    yew_router::hooks::use_navigator()
        .expect_throw("Unable to access history")
        .push(&ViewRoute::Apps);
    html! { <main></main> }
}

// Needed for yew to pass by value
#[allow(clippy::needless_pass_by_value)]
fn switch(route: Route) -> Html {
    #[cfg(debug_assertions)]
    console::debug!("manager::switch");
    match route {
        #[allow(clippy::let_unit_value)]
        Route::Landing => html! { <Landing /> },
        #[allow(clippy::let_unit_value)]
        Route::Login => html! { <LoginApp workflow={ LoginWorkflow::Login } /> },
        #[allow(clippy::let_unit_value)]
        Route::Reauth => html! { <LoginApp workflow={ LoginWorkflow::Reauth } /> },
        #[allow(clippy::let_unit_value)]
        Route::Oauth2 => html! { <Oauth2App /> },
        #[allow(clippy::let_unit_value)]
        Route::Views => html! { <ViewsApp /> },
        #[allow(clippy::let_unit_value)]
        Route::CredentialReset => html! { <CredentialResetApp /> },
        Route::NotFound => {
            add_body_form_classes!();

            html! {
                <>
                <main class="flex-shrink-0 form-signin text-center">
                        <img src="/pkg/img/logo-square.svg" alt="Kanidm" class="kanidm_logo"/>
                        // TODO: replace this with a call to domain info
                        <h3>{ "404 - Page not found" }</h3>

                        <div class="container">
                        <Link<ViewRoute> to={ ViewRoute::Apps }>
                        { "Home" }
                        </Link<ViewRoute>>
                        </div>
                </main>
                { crate::utils::do_footer() }
                </>
            }
        }
    }
}

pub struct ManagerApp {}

impl Component for ManagerApp {
    type Message = ();
    type Properties = ();

    fn create(_ctx: &Context<Self>) -> Self {
        #[cfg(debug_assertions)]
        console::debug!("manager::create");
        ManagerApp {}
    }

    fn changed(&mut self, _ctx: &Context<Self>, _props: &Self::Properties) -> bool {
        #[cfg(debug_assertions)]
        console::debug!("manager::change");
        false
    }

    fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
        #[cfg(debug_assertions)]
        console::debug!("manager::update");
        true
    }

    fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
        #[cfg(debug_assertions)]
        console::debug!("manager::rendered");
        // Can only access the current_route AFTER it renders.
        // console::debug!(format!("{:?}", yew_router::current_route::<Route>()).as_str())
    }

    fn view(&self, _ctx: &Context<Self>) -> Html {
        html! {
            <BrowserRouter>
                <Switch<Route> render={ switch } />
            </BrowserRouter>
        }
    }
}