diff --git a/Cargo.lock b/Cargo.lock index 1fa20eee8..f2bcf2ca5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2505,6 +2505,7 @@ dependencies = [ "serde", "serde-wasm-bindgen 0.4.5", "serde_json", + "url", "uuid", "wasm-bindgen", "wasm-bindgen-futures", @@ -5041,9 +5042,9 @@ dependencies = [ [[package]] name = "webauthn-authenticator-rs" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc33594bd26aabc90ce6d081d98617f5d16803660187f88912dead3ac2a9784" +checksum = "603b8602cae2d6c3706b6195765ff582389494d10c442d84a1de2ed5a25679ef" dependencies = [ "authenticator-ctap2-2021", "base64urlsafedata", @@ -5075,9 +5076,9 @@ dependencies = [ [[package]] name = "webauthn-rs-core" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef9a989b5cd2c39c52850c4bc36ebc88907a06ceacf7f80e45d78a308944b9d" +checksum = "294c78c83f12153a51e1cf1e6970b5da1397645dada39033a9c3173a8fc4fc2b" dependencies = [ "base64 0.13.1", "base64urlsafedata", @@ -5099,9 +5100,9 @@ dependencies = [ [[package]] name = "webauthn-rs-proto" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c7662de492733e6ea11e2726270bf34f5d7f8dbd08cd089830fbb3639a229" +checksum = "d24e638361a63ba5c0a0be6a60229490fcdf33740ed63df5bb6bdb627b52a138" dependencies = [ "base64urlsafedata", "js-sys", diff --git a/kanidm_proto/src/internal.rs b/kanidm_proto/src/internal.rs new file mode 100644 index 000000000..655b126b4 --- /dev/null +++ b/kanidm_proto/src/internal.rs @@ -0,0 +1,16 @@ + +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Debug, Serialize, Deserialize, Clone)] +/// This is a description of a linked or connected application for a user. This is +/// used in the UI to render applications on the dashboard for a user to access. +pub enum AppLink { + Oauth2 { + display_name: String, + redirect_url: Url, + // Future problem. + // icon: Icon, + + } +} diff --git a/kanidm_proto/src/lib.rs b/kanidm_proto/src/lib.rs index 1c6ebe6d9..f42588646 100644 --- a/kanidm_proto/src/lib.rs +++ b/kanidm_proto/src/lib.rs @@ -14,5 +14,6 @@ pub mod oauth2; pub mod scim_v1; pub mod utils; pub mod v1; +pub mod internal; pub use webauthn_rs_proto as webauthn; diff --git a/kanidmd/lib/src/ldap.rs b/kanidmd/lib/src/ldap.rs index a183dfb68..4639306aa 100644 --- a/kanidmd/lib/src/ldap.rs +++ b/kanidmd/lib/src/ldap.rs @@ -170,15 +170,23 @@ impl LdapServer { // Map the Some(a,v) to ...? let ext_filter = match (&sr.scope, req_dn) { - (LdapSearchScope::OneLevel, Some(_r)) => return Ok(vec![sr.gen_success()]), - (LdapSearchScope::OneLevel, None) => { + // OneLevel and Child searches are veerrrryyy similar for us because child + // is a "subtree search excluding base". Because we don't have a tree structure at + // all, this is the same as a onelevel (ald children of base excludeing base). + (LdapSearchScope::Children, Some(_r)) | (LdapSearchScope::OneLevel, Some(_r)) => { + return Ok(vec![sr.gen_success()]) + } + (LdapSearchScope::Children, None) | (LdapSearchScope::OneLevel, None) => { // exclude domain_info Some(LdapFilter::Not(Box::new(LdapFilter::Equality( "uuid".to_string(), STR_UUID_DOMAIN_INFO.to_string(), )))) } - (LdapSearchScope::Base, Some((a, v))) => Some(LdapFilter::Equality(a, v)), + // because we request a specific DN, these are the same since we want the same + // entry. + (LdapSearchScope::Base, Some((a, v))) + | (LdapSearchScope::Subtree, Some((a, v))) => Some(LdapFilter::Equality(a, v)), (LdapSearchScope::Base, None) => { // domain_info Some(LdapFilter::Equality( @@ -186,7 +194,6 @@ impl LdapServer { STR_UUID_DOMAIN_INFO.to_string(), )) } - (LdapSearchScope::Subtree, Some((a, v))) => Some(LdapFilter::Equality(a, v)), (LdapSearchScope::Subtree, None) => { // No filter changes needed. None diff --git a/kanidmd_web_ui/Cargo.toml b/kanidmd_web_ui/Cargo.toml index bf254dfd7..65ade062a 100644 --- a/kanidmd_web_ui/Cargo.toml +++ b/kanidmd_web_ui/Cargo.toml @@ -35,6 +35,7 @@ uuid = "^1.2.1" wasm-bindgen = { version = "^0.2.81" } wasm-bindgen-futures = { version = "^0.4.30" } wasm-bindgen-test = "0.3.33" +url = "^2.3.1" yew = "^0.19.3" yew-agent = "^0.1.0" yew-router = "^0.16.0" diff --git a/kanidmd_web_ui/pkg/kanidmd_web_ui.js b/kanidmd_web_ui/pkg/kanidmd_web_ui.js index 01b13f1e7..3b65d87b2 100644 --- a/kanidmd_web_ui/pkg/kanidmd_web_ui.js +++ b/kanidmd_web_ui/pkg/kanidmd_web_ui.js @@ -1040,16 +1040,16 @@ function getImports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper4757 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1013, __wbg_adapter_48); + imports.wbg.__wbindgen_closure_wrapper4782 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1015, __wbg_adapter_48); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper4941 = function(arg0, arg1, arg2) { - const ret = makeClosure(arg0, arg1, 1037, __wbg_adapter_51); + imports.wbg.__wbindgen_closure_wrapper4966 = function(arg0, arg1, arg2) { + const ret = makeClosure(arg0, arg1, 1039, __wbg_adapter_51); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper5597 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 1287, __wbg_adapter_54); + imports.wbg.__wbindgen_closure_wrapper5621 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 1288, __wbg_adapter_54); return addHeapObject(ret); }; diff --git a/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm b/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm index 53186f834..ab2708ac5 100644 Binary files a/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm and b/kanidmd_web_ui/pkg/kanidmd_web_ui_bg.wasm differ diff --git a/kanidmd_web_ui/src/components/admin_accounts.rs b/kanidmd_web_ui/src/components/admin_accounts.rs index d2a6c1e01..9b6b3b3bb 100644 --- a/kanidmd_web_ui/src/components/admin_accounts.rs +++ b/kanidmd_web_ui/src/components/admin_accounts.rs @@ -4,7 +4,7 @@ use gloo::console; use yew::{html, Component, Context, Html, Properties}; use yew_router::prelude::Link; -use crate::components::adminmenu::{Entity, EntityType, GetError}; +use crate::components::admin_menu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; use crate::constants::{ CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_DT, CSS_TABLE, diff --git a/kanidmd_web_ui/src/components/admin_groups.rs b/kanidmd_web_ui/src/components/admin_groups.rs index ba1096186..ebcf08bc9 100644 --- a/kanidmd_web_ui/src/components/admin_groups.rs +++ b/kanidmd_web_ui/src/components/admin_groups.rs @@ -4,7 +4,7 @@ use gloo::console; use yew::{html, Component, Context, Html, Properties}; use yew_router::prelude::Link; -use crate::components::adminmenu::{Entity, EntityType, GetError}; +use crate::components::admin_menu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; use crate::utils::{do_alert_error, do_page_header, init_request}; diff --git a/kanidmd_web_ui/src/components/adminmenu.rs b/kanidmd_web_ui/src/components/admin_menu.rs similarity index 96% rename from kanidmd_web_ui/src/components/adminmenu.rs rename to kanidmd_web_ui/src/components/admin_menu.rs index c298e27cc..c79b40533 100644 --- a/kanidmd_web_ui/src/components/adminmenu.rs +++ b/kanidmd_web_ui/src/components/admin_menu.rs @@ -3,13 +3,10 @@ use yew::{html, Component, Context, Html, Properties}; use yew_router::prelude::Link; use crate::components::alpha_warning_banner; -use crate::constants::{CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER}; +use crate::constants::{CSS_CARD, CSS_CARD_BODY, CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER}; // use crate::error::FetchError; use crate::views::AdminRoute; -const CSS_CARD: &str = "card text-center"; -const CSS_CARD_BODY: &str = "card-body text-center"; - #[derive(Eq, PartialEq, Properties)] pub struct Props; diff --git a/kanidmd_web_ui/src/components/admin_oauth2.rs b/kanidmd_web_ui/src/components/admin_oauth2.rs index a54b571e0..591b68ad7 100644 --- a/kanidmd_web_ui/src/components/admin_oauth2.rs +++ b/kanidmd_web_ui/src/components/admin_oauth2.rs @@ -4,7 +4,7 @@ use gloo::console; use yew::{html, Component, Context, Html, Properties}; use yew_router::prelude::Link; -use crate::components::adminmenu::{Entity, EntityType, GetError}; +use crate::components::admin_menu::{Entity, EntityType, GetError}; use crate::components::alpha_warning_banner; use crate::constants::{CSS_BREADCRUMB_ITEM, CSS_BREADCRUMB_ITEM_ACTIVE, CSS_CELL, CSS_TABLE}; use crate::utils::{do_alert_error, do_page_header, init_request}; diff --git a/kanidmd_web_ui/src/components/mod.rs b/kanidmd_web_ui/src/components/mod.rs index 395fdd97a..62c376d6f 100644 --- a/kanidmd_web_ui/src/components/mod.rs +++ b/kanidmd_web_ui/src/components/mod.rs @@ -3,8 +3,8 @@ use yew::Html; pub mod admin_accounts; pub mod admin_groups; +pub mod admin_menu; pub mod admin_oauth2; -pub mod adminmenu; pub mod change_unix_password; /// creates the "Kanidm is alpha" banner diff --git a/kanidmd_web_ui/src/constants.rs b/kanidmd_web_ui/src/constants.rs index 86c5847c0..d4e156fea 100644 --- a/kanidmd_web_ui/src/constants.rs +++ b/kanidmd_web_ui/src/constants.rs @@ -30,3 +30,7 @@ pub const CSS_DT: &str = "col-6"; pub const CSS_BREADCRUMB_ITEM: &str = "breadcrumb-item"; pub const CSS_BREADCRUMB_ITEM_ACTIVE: &str = "breadcrumb-item active"; + +// used in the UI for ... cards +pub const CSS_CARD: &str = "card text-center"; +pub const CSS_CARD_BODY: &str = "card-body text-center"; diff --git a/kanidmd_web_ui/src/oauth2.rs b/kanidmd_web_ui/src/oauth2.rs index a81ef2dfa..70ccf6200 100644 --- a/kanidmd_web_ui/src/oauth2.rs +++ b/kanidmd_web_ui/src/oauth2.rs @@ -244,9 +244,7 @@ impl Component for Oauth2App { }; } }; - - let e_msg = format!("{:?}", query); - console::error!(e_msg.as_str()); + console::debug!(format!("{query:?}", )); // In the query, if this is openid there MAY be a hint // as to the users name. diff --git a/kanidmd_web_ui/src/views/apps.rs b/kanidmd_web_ui/src/views/apps.rs index 2a9ae6f7b..ef446c681 100644 --- a/kanidmd_web_ui/src/views/apps.rs +++ b/kanidmd_web_ui/src/views/apps.rs @@ -3,22 +3,58 @@ use gloo::console; use yew::prelude::*; use crate::components::alpha_warning_banner; -use crate::constants::{CSS_CELL, CSS_PAGE_HEADER, CSS_TABLE}; +use crate::constants::{CSS_CARD, CSS_LINK_DARK_STRETCHED, CSS_PAGE_HEADER, CSS_CARD_BODY}; +use crate::error::FetchError; +use wasm_bindgen::prelude::*; +// use crate::utils; +// use wasm_bindgen::JsCast; +// use wasm_bindgen_futures::JsFuture; +// use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; + +use kanidm_proto::internal::AppLink; pub enum Msg { - // Nothing + Ready { apps: Vec }, + Error { emsg: String, kopid: Option }, } -pub struct AppsApp {} +impl From for Msg { + fn from(fe: FetchError) -> Self { + Msg::Error { + emsg: fe.as_string(), + kopid: None, + } + } +} + +pub enum State { + Waiting, + Ready { apps: Vec }, + Error { emsg: String, kopid: Option }, +} + +pub struct AppsApp { + state: State, +} impl Component for AppsApp { type Message = Msg; type Properties = (); - fn create(_ctx: &Context) -> Self { + fn create(ctx: &Context) -> Self { #[cfg(debug)] console::debug!("views::apps::create"); - AppsApp {} + + ctx.link().send_future(async { + match Self::fetch_user_apps().await { + Ok(v) => v, + Err(v) => v.into(), + } + }); + + let state = State::Waiting; + + AppsApp { state } } fn changed(&mut self, _ctx: &Context) -> bool { @@ -27,15 +63,14 @@ impl Component for AppsApp { false } - fn update(&mut self, _ctx: &Context, _msg: Self::Message) -> bool { + fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { #[cfg(debug)] console::debug!("views::apps::update"); - /* match msg { - ViewsMsg::Logout => { - } + Msg::Ready { apps } => self.state = State::Ready { apps }, + Msg::Error { emsg, kopid } => self.state = State::Error { emsg, kopid }, } - */ + true } @@ -44,44 +79,138 @@ impl Component for AppsApp { console::debug!("views::apps::rendered"); } - fn view(&self, _ctx: &Context) -> Html { + fn view(&self, ctx: &Context) -> Html { + match &self.state { + State::Waiting => self.view_waiting(), + State::Ready { apps } => self.view_ready(ctx, apps.as_slice()), + State::Error { emsg, kopid } => self.view_error(ctx, &emsg, kopid.as_deref()), + } + } +} + +impl AppsApp { + fn view_waiting(&self) -> Html { html! { <> -
-

{ "Apps" }

-
- - { alpha_warning_banner() } -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
{ "#" }{ "Header" }{ "Header" }{ "Header" }{ "Header" }
{ "1,001" }{ "random" }{ "data" }{ "placeholder" }{ "text" }
{ "1,015" }{ "random" }{ "tabular" }{ "informaasdftion" }{ "text" }
+
+
+ { "Loading..." } +
} } + + fn view_ready(&self, _ctx: &Context, apps: &[AppLink]) -> Html { + // Please help me, I don't know how to make a grid look nice 🥺 + html! { + <> +
+

{ "Applications list" }

+
+ { alpha_warning_banner() } + if !apps.is_empty() { +
+ { + apps.iter().map(|applink| { + match &applink { + AppLink::Oauth2 { + display_name, redirect_url + } => { + let redirect_url = redirect_url.to_string(); + html!{ +
+
+

+ { display_name } +

+
{ "We could put some text here but that's only if there was an app description!"} +
+ // + +
+
+ } + } + } + }).collect::() + } +
+ } + + } + } + + fn view_error(&self, _ctx: &Context, msg: &str, kopid: Option<&str>) -> Html { + html! { + <> +

+ +

+ +

+ +

+ + } + } + + async fn fetch_user_apps() -> Result { + // WILLIAM TODO - Add an api end point to get these applinks from + // kanidm based on what you can access. + + /* + let mut opts = RequestInit::new(); + opts.method("GET"); + opts.mode(RequestMode::SameOrigin); + opts.credentials(RequestCredentials::SameOrigin); + + let request = Request::new_with_str_and_init("/no/such/route", &opts)?; + + request + .headers() + .set("content-type", "application/json") + .expect_throw("failed to set header"); + + let window = utils::window(); + let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; + let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type"); + let status = resp.status(); + + if status == 200 { + let jsval = JsFuture::from(resp.json()?).await?; + let apps: Vec<()> = serde_wasm_bindgen::from_value(jsval) + .expect_throw("Invalid response type - auth_init::AuthResponse"); + Ok(Msg::Ready { apps }) + } else { + let headers = resp.headers(); + let kopid = headers.get("x-kanidm-opid").ok().flatten(); + let text = JsFuture::from(resp.text()?).await?; + let emsg = text.as_string().unwrap_or_default(); + Ok(Msg::Error { emsg, kopid }) + } + */ + + Ok(Msg::Ready { + apps: vec![AppLink::Oauth2 { + display_name: "Test Application".to_string(), + redirect_url: url::Url::parse("http://localhost:8080") + .expect_throw("Failed to setup url"), + }], + }) + } } diff --git a/kanidmd_web_ui/src/views/mod.rs b/kanidmd_web_ui/src/views/mod.rs index 9e165f4c0..39f6f7a9f 100644 --- a/kanidmd_web_ui/src/views/mod.rs +++ b/kanidmd_web_ui/src/views/mod.rs @@ -1,5 +1,5 @@ use gloo::console; -use kanidm_proto::v1::{UserAuthToken, UiHint}; +use kanidm_proto::v1::{UiHint, UserAuthToken}; use serde::{Deserialize, Serialize}; use wasm_bindgen::{JsCast, UnwrapThrowExt}; use wasm_bindgen_futures::JsFuture; @@ -7,7 +7,7 @@ use web_sys::{Request, RequestCredentials, RequestInit, RequestMode, Response}; use yew::prelude::*; use yew_router::prelude::*; -use crate::components::{admin_accounts, admin_groups, admin_oauth2, adminmenu}; +use crate::components::{admin_accounts, admin_groups, admin_menu, admin_oauth2}; use crate::error::*; use crate::manager::Route; use crate::{models, utils}; @@ -444,7 +444,7 @@ impl ViewsApp { fn admin_routes(route: &AdminRoute) -> Html { match route { AdminRoute::AdminMenu => html! { - + }, AdminRoute::AdminListAccounts => html!(