mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Add auth session header type (#398)
This commit is contained in:
parent
8a2f3b65ec
commit
988944a085
143
Cargo.lock
generated
143
Cargo.lock
generated
|
@ -82,9 +82,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.39"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
|
||||
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
|
||||
|
||||
[[package]]
|
||||
name = "anymap"
|
||||
|
@ -273,7 +273,7 @@ dependencies = [
|
|||
"async-io",
|
||||
"async-lock",
|
||||
"async-process",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
|
@ -512,11 +512,11 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
|||
|
||||
[[package]]
|
||||
name = "byte-pool"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38e98299d518ec351ca016363e0cbfc77059dcd08dfa9700d15e405536097a"
|
||||
checksum = "f8c7230ddbb427b1094d477d821a99f3f54d36333178eeb806e279bcdcecf0ca"
|
||||
dependencies = [
|
||||
"crossbeam-queue 0.2.3",
|
||||
"crossbeam-queue",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
|
@ -634,7 +634,7 @@ dependencies = [
|
|||
"ahash 0.7.2",
|
||||
"crossbeam",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
"num",
|
||||
"packed_simd_2",
|
||||
"parking_lot",
|
||||
|
@ -663,9 +663,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
|
||||
checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
|
@ -780,8 +780,8 @@ dependencies = [
|
|||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue 0.3.1",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -791,7 +791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -802,7 +802,7 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
|||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -812,23 +812,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.1"
|
||||
|
@ -836,18 +825,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils 0.8.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1414,9 +1392,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d832b01df74254fe364568d6ddc294443f61cbec82816b60904303af87efae78"
|
||||
checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
|
@ -1571,9 +1549,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.4"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
|
||||
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -1586,7 +1564,7 @@ dependencies = [
|
|||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project",
|
||||
"socket2 0.3.19",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -1713,9 +1691,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.49"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821"
|
||||
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1923,9 +1901,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.91"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7"
|
||||
checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
|
@ -2023,12 +2001,6 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
|
@ -2037,9 +2009,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
|||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
|
||||
checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -2097,7 +2069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a19900e7eee95eb2b3c2e26d12a874cc80aaf750e31be6fcbe743ead369fa45d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"socket2 0.4.0",
|
||||
"socket2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2384,18 +2356,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63"
|
||||
checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
|
||||
checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2691,7 +2663,7 @@ checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
|||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils 0.8.3",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
@ -2915,9 +2887,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.1.2"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d"
|
||||
checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
|
@ -2928,9 +2900,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d"
|
||||
checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -3118,17 +3090,6 @@ dependencies = [
|
|||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.0"
|
||||
|
@ -3270,9 +3231,9 @@ checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.64"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||
checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3734,9 +3695,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.72"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe"
|
||||
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"serde",
|
||||
|
@ -3746,9 +3707,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.72"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3"
|
||||
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
|
@ -3761,9 +3722,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468"
|
||||
checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
|
@ -3773,9 +3734,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.72"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b"
|
||||
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -3783,9 +3744,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.72"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d"
|
||||
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3796,15 +3757,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.72"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa"
|
||||
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.49"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310"
|
||||
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
|
@ -21,6 +21,7 @@ toml = "0.5"
|
|||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
url = "2.1.1"
|
||||
webauthn-rs = "0.3.0-alpha.7"
|
||||
tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
use crate::{ClientError, KanidmClientBuilder, APPLICATION_JSON, KOPID};
|
||||
use crate::{ClientError, KanidmClientBuilder, APPLICATION_JSON, KOPID, KSESSIONID};
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet as Set;
|
||||
use uuid::Uuid;
|
||||
|
||||
use webauthn_rs::proto::{
|
||||
CreationChallengeResponse, PublicKeyCredential, RegisterPublicKeyCredential,
|
||||
RequestChallengeResponse,
|
||||
};
|
||||
|
||||
use kanidm_proto::v1::*;
|
||||
|
||||
|
@ -10,11 +17,107 @@ use kanidm_proto::v1::*;
|
|||
pub struct KanidmAsyncClient {
|
||||
pub(crate) client: reqwest::Client,
|
||||
pub(crate) addr: String,
|
||||
pub(crate) origin: String,
|
||||
pub(crate) builder: KanidmClientBuilder,
|
||||
pub(crate) bearer_token: Option<String>,
|
||||
pub(crate) auth_session_id: Option<String>,
|
||||
}
|
||||
|
||||
impl KanidmAsyncClient {
|
||||
pub fn get_origin(&self) -> &str {
|
||||
self.origin.as_str()
|
||||
}
|
||||
|
||||
pub fn set_token(&mut self, new_token: String) {
|
||||
let mut new_token = Some(new_token);
|
||||
std::mem::swap(&mut self.bearer_token, &mut new_token);
|
||||
}
|
||||
|
||||
pub fn get_token(&self) -> Option<&str> {
|
||||
self.bearer_token.as_deref()
|
||||
}
|
||||
|
||||
pub fn new_session(&self) -> Result<Self, reqwest::Error> {
|
||||
// Copy our builder, and then just process it.
|
||||
let builder = self.builder.clone();
|
||||
builder.build_async()
|
||||
}
|
||||
|
||||
pub fn logout(&mut self) -> Result<(), reqwest::Error> {
|
||||
// hack - we have to replace our reqwest client because that's the only way
|
||||
// to currently flush the cookie store. To achieve this we need to rebuild
|
||||
// and then destructure.
|
||||
let builder = self.builder.clone();
|
||||
let KanidmAsyncClient { mut client, .. } = builder.build_async()?;
|
||||
|
||||
std::mem::swap(&mut self.client, &mut client);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn perform_auth_post_request<R: Serialize, T: DeserializeOwned>(
|
||||
&mut self,
|
||||
dest: &str,
|
||||
request: R,
|
||||
) -> Result<T, ClientError> {
|
||||
let dest = [self.addr.as_str(), dest].concat();
|
||||
debug!("{:?}", dest);
|
||||
// format doesn't work in async ?!
|
||||
// let dest = format!("{}{}", self.addr, dest);
|
||||
|
||||
let req_string = serde_json::to_string(&request).map_err(ClientError::JSONEncode)?;
|
||||
|
||||
let response = self
|
||||
.client
|
||||
.post(dest.as_str())
|
||||
.body(req_string)
|
||||
.header(CONTENT_TYPE, APPLICATION_JSON);
|
||||
let response = if let Some(token) = &self.bearer_token {
|
||||
response.bearer_auth(token)
|
||||
} else {
|
||||
response
|
||||
};
|
||||
|
||||
// If we have a session header, set it now.
|
||||
let response = if let Some(sessionid) = &self.auth_session_id {
|
||||
response.header(KSESSIONID, sessionid)
|
||||
} else {
|
||||
response
|
||||
};
|
||||
|
||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||
|
||||
// If we have a sessionid header in the response, get it now.
|
||||
|
||||
let headers = response.headers();
|
||||
|
||||
self.auth_session_id = headers
|
||||
.get(KSESSIONID)
|
||||
.map(|hv| hv.to_str().ok().map(|s| s.to_string()))
|
||||
.flatten();
|
||||
|
||||
let opid = headers
|
||||
.get(KOPID)
|
||||
.and_then(|hv| hv.to_str().ok().map(|s| s.to_string()))
|
||||
.unwrap_or_else(|| "missing_kopid".to_string());
|
||||
debug!("opid -> {:?}", opid);
|
||||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => {}
|
||||
unexpect => {
|
||||
return Err(ClientError::Http(
|
||||
unexpect,
|
||||
response.json().await.ok(),
|
||||
opid,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| ClientError::JSONDecode(e, opid))
|
||||
}
|
||||
|
||||
async fn perform_post_request<R: Serialize, T: DeserializeOwned>(
|
||||
&self,
|
||||
dest: &str,
|
||||
|
@ -190,14 +293,16 @@ impl KanidmAsyncClient {
|
|||
.map_err(|e| ClientError::JSONDecode(e, opid))
|
||||
}
|
||||
|
||||
pub async fn auth_step_init(&self, ident: &str) -> Result<Set<AuthMech>, ClientError> {
|
||||
pub async fn auth_step_init(&mut self, ident: &str) -> Result<Set<AuthMech>, ClientError> {
|
||||
let auth_init = AuthRequest {
|
||||
step: AuthStep::Init(ident.to_string()),
|
||||
};
|
||||
|
||||
let r: Result<AuthResponse, _> = self.perform_post_request("/v1/auth", auth_init).await;
|
||||
let r: Result<AuthResponse, _> =
|
||||
self.perform_auth_post_request("/v1/auth", auth_init).await;
|
||||
r.map(|v| {
|
||||
debug!("Authentication Session ID -> {:?}", v.sessionid);
|
||||
// Stash the session ID header.
|
||||
v.state
|
||||
})
|
||||
.and_then(|state| match state {
|
||||
|
@ -207,12 +312,16 @@ impl KanidmAsyncClient {
|
|||
.map(|mechs| mechs.into_iter().collect())
|
||||
}
|
||||
|
||||
pub async fn auth_step_begin(&self, mech: AuthMech) -> Result<Vec<AuthAllowed>, ClientError> {
|
||||
pub async fn auth_step_begin(
|
||||
&mut self,
|
||||
mech: AuthMech,
|
||||
) -> Result<Vec<AuthAllowed>, ClientError> {
|
||||
let auth_begin = AuthRequest {
|
||||
step: AuthStep::Begin(mech),
|
||||
};
|
||||
|
||||
let r: Result<AuthResponse, _> = self.perform_post_request("/v1/auth", auth_begin).await;
|
||||
let r: Result<AuthResponse, _> =
|
||||
self.perform_auth_post_request("/v1/auth", auth_begin).await;
|
||||
r.map(|v| {
|
||||
debug!("Authentication Session ID -> {:?}", v.sessionid);
|
||||
v.state
|
||||
|
@ -225,6 +334,96 @@ impl KanidmAsyncClient {
|
|||
// .map(|allowed| allowed.into_iter().collect())
|
||||
}
|
||||
|
||||
pub async fn auth_step_anonymous(&mut self) -> Result<AuthResponse, ClientError> {
|
||||
let auth_anon = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Anonymous),
|
||||
};
|
||||
let r: Result<AuthResponse, _> =
|
||||
self.perform_auth_post_request("/v1/auth", auth_anon).await;
|
||||
|
||||
r.map(|ar| {
|
||||
if let AuthState::Success(token) = &ar.state {
|
||||
self.bearer_token = Some(token.clone());
|
||||
};
|
||||
ar
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn auth_step_password(
|
||||
&mut self,
|
||||
password: &str,
|
||||
) -> Result<AuthResponse, ClientError> {
|
||||
let auth_req = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Password(password.to_string())),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_auth_post_request("/v1/auth", auth_req).await;
|
||||
|
||||
r.map(|ar| {
|
||||
if let AuthState::Success(token) = &ar.state {
|
||||
self.bearer_token = Some(token.clone());
|
||||
};
|
||||
ar
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn auth_step_totp(&mut self, totp: u32) -> Result<AuthResponse, ClientError> {
|
||||
let auth_req = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::TOTP(totp)),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_auth_post_request("/v1/auth", auth_req).await;
|
||||
|
||||
r.map(|ar| {
|
||||
if let AuthState::Success(token) = &ar.state {
|
||||
self.bearer_token = Some(token.clone());
|
||||
};
|
||||
ar
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn auth_step_webauthn_complete(
|
||||
&mut self,
|
||||
pkc: PublicKeyCredential,
|
||||
) -> Result<AuthResponse, ClientError> {
|
||||
let auth_req = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Webauthn(pkc)),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_auth_post_request("/v1/auth", auth_req).await;
|
||||
|
||||
r.map(|ar| {
|
||||
if let AuthState::Success(token) = &ar.state {
|
||||
self.bearer_token = Some(token.clone());
|
||||
};
|
||||
ar
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn auth_anonymous(&mut self) -> Result<(), ClientError> {
|
||||
let mechs = match self.auth_step_init("anonymous").await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !mechs.contains(&AuthMech::Anonymous) {
|
||||
debug!("Anonymous mech not presented");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let _state = match self.auth_step_begin(AuthMech::Anonymous).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let r = self.auth_step_anonymous().await?;
|
||||
|
||||
match r.state {
|
||||
AuthState::Success(token) => {
|
||||
self.bearer_token = Some(token);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_simple_password(
|
||||
&mut self,
|
||||
ident: &str,
|
||||
|
@ -245,50 +444,97 @@ impl KanidmAsyncClient {
|
|||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let auth_req = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Password(password.to_string())),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_post_request("/v1/auth", auth_req).await;
|
||||
|
||||
let r = r?;
|
||||
let r = self.auth_step_password(password).await?;
|
||||
|
||||
match r.state {
|
||||
AuthState::Success(token) => {
|
||||
self.bearer_token = Some(token);
|
||||
Ok(())
|
||||
}
|
||||
AuthState::Success(_) => Ok(()),
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_anonymous(&mut self) -> Result<(), ClientError> {
|
||||
let mechs = match self.auth_step_init("anonymous").await {
|
||||
pub async fn auth_password_totp(
|
||||
&mut self,
|
||||
ident: &str,
|
||||
password: &str,
|
||||
totp: u32,
|
||||
) -> Result<(), ClientError> {
|
||||
let mechs = match self.auth_step_init(ident).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !mechs.contains(&AuthMech::Anonymous) {
|
||||
debug!("Anonymous mech not presented");
|
||||
if !mechs.contains(&AuthMech::PasswordMFA) {
|
||||
debug!("PasswordMFA mech not presented");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let _state = match self.auth_step_begin(AuthMech::Anonymous).await {
|
||||
let state = match self.auth_step_begin(AuthMech::PasswordMFA).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let auth_anon = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::Anonymous),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_post_request("/v1/auth", auth_anon).await;
|
||||
if !state.contains(&AuthAllowed::TOTP) {
|
||||
debug!("TOTP step not offered.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let r = r?;
|
||||
let r = self.auth_step_totp(totp).await?;
|
||||
|
||||
// Should need to continue.
|
||||
match r.state {
|
||||
AuthState::Continue(allowed) => {
|
||||
if !allowed.contains(&AuthAllowed::Password) {
|
||||
debug!("Password step not offered.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("Invalid AuthState presented.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
};
|
||||
|
||||
let r = self.auth_step_password(password).await?;
|
||||
|
||||
match r.state {
|
||||
AuthState::Success(token) => {
|
||||
self.bearer_token = Some(token);
|
||||
Ok(())
|
||||
}
|
||||
AuthState::Success(_token) => Ok(()),
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_webauthn_begin(
|
||||
&mut self,
|
||||
ident: &str,
|
||||
) -> Result<RequestChallengeResponse, ClientError> {
|
||||
let mechs = match self.auth_step_init(ident).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !mechs.contains(&AuthMech::Webauthn) {
|
||||
debug!("Webauthn mech not presented");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let mut state = match self.auth_step_begin(AuthMech::Webauthn).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
// State is now a set of auth continues.
|
||||
match state.pop() {
|
||||
Some(AuthAllowed::Webauthn(r)) => Ok(r),
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_webauthn_complete(
|
||||
&mut self,
|
||||
pkc: PublicKeyCredential,
|
||||
) -> Result<(), ClientError> {
|
||||
let r = self.auth_step_webauthn_complete(pkc).await?;
|
||||
match r.state {
|
||||
AuthState::Success(_token) => Ok(()),
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
@ -297,12 +543,15 @@ impl KanidmAsyncClient {
|
|||
let whoami_dest = [self.addr.as_str(), "/v1/self"].concat();
|
||||
// format!("{}/v1/self", self.addr);
|
||||
debug!("{:?}", whoami_dest);
|
||||
let response = self
|
||||
.client
|
||||
.get(whoami_dest.as_str())
|
||||
.send()
|
||||
.await
|
||||
.map_err(ClientError::Transport)?;
|
||||
let response = self.client.get(whoami_dest.as_str());
|
||||
|
||||
let response = if let Some(token) = &self.bearer_token {
|
||||
response.bearer_auth(token)
|
||||
} else {
|
||||
response
|
||||
};
|
||||
|
||||
let response = response.send().await.map_err(ClientError::Transport)?;
|
||||
|
||||
let opid = response
|
||||
.headers()
|
||||
|
@ -332,6 +581,170 @@ impl KanidmAsyncClient {
|
|||
Ok(Some((r.youare, r.uat)))
|
||||
}
|
||||
|
||||
// Raw DB actions
|
||||
pub async fn search(&self, filter: Filter) -> Result<Vec<Entry>, ClientError> {
|
||||
let sr = SearchRequest { filter };
|
||||
let r: Result<SearchResponse, _> = self.perform_post_request("/v1/raw/search", sr).await;
|
||||
r.map(|v| v.entries)
|
||||
}
|
||||
|
||||
pub async fn create(&self, entries: Vec<Entry>) -> Result<bool, ClientError> {
|
||||
let c = CreateRequest { entries };
|
||||
let r: Result<OperationResponse, _> = self.perform_post_request("/v1/raw/create", c).await;
|
||||
r.map(|_| true)
|
||||
}
|
||||
|
||||
pub async fn modify(&self, filter: Filter, modlist: ModifyList) -> Result<bool, ClientError> {
|
||||
let mr = ModifyRequest { filter, modlist };
|
||||
let r: Result<OperationResponse, _> = self.perform_post_request("/v1/raw/modify", mr).await;
|
||||
r.map(|_| true)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, filter: Filter) -> Result<bool, ClientError> {
|
||||
let dr = DeleteRequest { filter };
|
||||
let r: Result<OperationResponse, _> = self.perform_post_request("/v1/raw/delete", dr).await;
|
||||
r.map(|_| true)
|
||||
}
|
||||
|
||||
// === idm actions here ==
|
||||
|
||||
// ===== GROUPS
|
||||
pub async fn idm_group_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/group").await
|
||||
}
|
||||
|
||||
pub async fn idm_group_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/group/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_get_members(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<Option<Vec<String>>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/group/{}/_attr/member", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_create(&self, name: &str) -> Result<bool, ClientError> {
|
||||
let mut new_group = Entry {
|
||||
attrs: BTreeMap::new(),
|
||||
};
|
||||
new_group
|
||||
.attrs
|
||||
.insert("name".to_string(), vec![name.to_string()]);
|
||||
self.perform_post_request("/v1/group", new_group)
|
||||
.await
|
||||
.map(|_: OperationResponse| true)
|
||||
}
|
||||
|
||||
pub async fn idm_group_set_members(
|
||||
&self,
|
||||
id: &str,
|
||||
members: &[&str],
|
||||
) -> Result<bool, ClientError> {
|
||||
let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect();
|
||||
self.perform_put_request(format!("/v1/group/{}/_attr/member", id).as_str(), m)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_add_members(
|
||||
&self,
|
||||
id: &str,
|
||||
members: &[&str],
|
||||
) -> Result<bool, ClientError> {
|
||||
let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect();
|
||||
self.perform_post_request(["/v1/group/", id, "/_attr/member"].concat().as_str(), m)
|
||||
.await
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn idm_group_remove_member(&self, id: &str, member: &str) -> Result<(), ClientError> {
|
||||
unimplemented!();
|
||||
}
|
||||
*/
|
||||
|
||||
pub async fn idm_group_purge_members(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(format!("/v1/group/{}/_attr/member", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_unix_extend(
|
||||
&self,
|
||||
id: &str,
|
||||
gidnumber: Option<u32>,
|
||||
) -> Result<bool, ClientError> {
|
||||
let gx = GroupUnixExtend { gidnumber };
|
||||
self.perform_post_request(format!("/v1/group/{}/_unix", id).as_str(), gx)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_unix_token_get(&self, id: &str) -> Result<UnixGroupToken, ClientError> {
|
||||
// Format doesn't work in async
|
||||
// format!("/v1/account/{}/_unix/_token", id).as_str()
|
||||
self.perform_get_request(["/v1/group/", id, "/_unix/_token"].concat().as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_delete(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(["/v1/group/", id].concat().as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
// ==== ACCOUNTS
|
||||
pub async fn idm_account_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/account").await
|
||||
}
|
||||
|
||||
pub async fn idm_account_create(&self, name: &str, dn: &str) -> Result<bool, ClientError> {
|
||||
let mut new_acct = Entry {
|
||||
attrs: BTreeMap::new(),
|
||||
};
|
||||
new_acct
|
||||
.attrs
|
||||
.insert("name".to_string(), vec![name.to_string()]);
|
||||
new_acct
|
||||
.attrs
|
||||
.insert("displayname".to_string(), vec![dn.to_string()]);
|
||||
self.perform_post_request("/v1/account", new_acct)
|
||||
.await
|
||||
.map(|_: OperationResponse| true)
|
||||
}
|
||||
|
||||
pub async fn idm_account_set_password(&self, cleartext: String) -> Result<bool, ClientError> {
|
||||
let s = SingleStringRequest { value: cleartext };
|
||||
|
||||
let r: Result<OperationResponse, _> = self
|
||||
.perform_post_request("/v1/self/_credential/primary/set_password", s)
|
||||
.await;
|
||||
r.map(|_| true)
|
||||
}
|
||||
|
||||
pub async fn idm_account_set_displayname(
|
||||
&self,
|
||||
id: &str,
|
||||
dn: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
self.idm_account_set_attr(id, "displayname", &[dn]).await
|
||||
}
|
||||
|
||||
pub async fn idm_account_unix_token_get(&self, id: &str) -> Result<UnixUserToken, ClientError> {
|
||||
// Format doesn't work in async
|
||||
// format!("/v1/account/{}/_unix/_token", id).as_str()
|
||||
self.perform_get_request(["/v1/account/", id, "/_unix/_token"].concat().as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_delete(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(["/v1/account/", id].concat().as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_set_attr(
|
||||
&self,
|
||||
id: &str,
|
||||
|
@ -343,27 +756,240 @@ impl KanidmAsyncClient {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_unix_token_get(&self, id: &str) -> Result<UnixUserToken, ClientError> {
|
||||
// Format doesn't work in async
|
||||
// format!("/v1/account/{}/_unix/_token", id).as_str()
|
||||
self.perform_get_request(["/v1/account/", id, "/_unix/_token"].concat().as_str())
|
||||
pub async fn idm_account_get_attr(
|
||||
&self,
|
||||
id: &str,
|
||||
attr: &str,
|
||||
) -> Result<Option<Vec<String>>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}/_attr/{}", id, attr).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_unix_token_get(&self, id: &str) -> Result<UnixGroupToken, ClientError> {
|
||||
// Format doesn't work in async
|
||||
// format!("/v1/account/{}/_unix/_token", id).as_str()
|
||||
self.perform_get_request(["/v1/group/", id, "/_unix/_token"].concat().as_str())
|
||||
pub async fn idm_account_purge_attr(&self, id: &str, attr: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(format!("/v1/account/{}/_attr/{}", id, attr).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_delete(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(["/v1/account/", id].concat().as_str())
|
||||
pub async fn idm_account_primary_credential_set_password(
|
||||
&self,
|
||||
id: &str,
|
||||
pw: &str,
|
||||
) -> Result<SetCredentialResponse, ClientError> {
|
||||
let r = SetCredentialRequest::Password(pw.to_string());
|
||||
self.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_import_password(
|
||||
&self,
|
||||
id: &str,
|
||||
pw: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
self.perform_put_request(
|
||||
format!("/v1/account/{}/_attr/password_import", id).as_str(),
|
||||
vec![pw.to_string()],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_set_generated(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<String, ClientError> {
|
||||
let r = SetCredentialRequest::GeneratePassword;
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Token(p)) => Ok(p),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// Reg intent for totp
|
||||
pub async fn idm_account_primary_credential_generate_totp(
|
||||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, TOTPSecret), ClientError> {
|
||||
let r = SetCredentialRequest::TOTPGenerate(label.to_string());
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::TOTPCheck(u, s)) => Ok((u, s)),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the totp
|
||||
pub async fn idm_account_primary_credential_verify_totp(
|
||||
&self,
|
||||
id: &str,
|
||||
otp: u32,
|
||||
session: Uuid,
|
||||
) -> Result<bool, ClientError> {
|
||||
let r = SetCredentialRequest::TOTPVerify(session, otp);
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Success) => Ok(true),
|
||||
Ok(SetCredentialResponse::TOTPCheck(u, s)) => Err(ClientError::TOTPVerifyFailed(u, s)),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_remove_totp(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
let r = SetCredentialRequest::TOTPRemove;
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Success) => Ok(true),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_register_webauthn(
|
||||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, CreationChallengeResponse), ClientError> {
|
||||
let r = SetCredentialRequest::WebauthnBegin(label.to_string());
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::WebauthnCreateChallenge(u, s)) => Ok((u, s)),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_complete_webuthn_registration(
|
||||
&self,
|
||||
id: &str,
|
||||
rego: RegisterPublicKeyCredential,
|
||||
session: Uuid,
|
||||
) -> Result<(), ClientError> {
|
||||
let r = SetCredentialRequest::WebauthnRegister(session, rego);
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Success) => Ok(()),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn idm_account_primary_credential_remove_webauthn(
|
||||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
let r = SetCredentialRequest::WebauthnRemove(label.to_string());
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
r,
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Success) => Ok(true),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn idm_account_get_credential_status(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<CredentialStatus, ClientError> {
|
||||
let res: Result<CredentialStatus, ClientError> = self
|
||||
.perform_get_request(format!("/v1/account/{}/_credential/_status", id).as_str())
|
||||
.await;
|
||||
res.and_then(|cs| {
|
||||
if cs.creds.is_empty() {
|
||||
Err(ClientError::EmptyResponse)
|
||||
} else {
|
||||
Ok(cs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn idm_account_radius_credential_get(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<Option<String>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}/_radius", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_delete(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(["/v1/group/", id].concat().as_str())
|
||||
pub async fn idm_account_radius_credential_regenerate(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<String, ClientError> {
|
||||
self.perform_post_request(format!("/v1/account/{}/_radius", id).as_str(), ())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_radius_credential_delete(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(format!("/v1/account/{}/_radius", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_radius_token_get(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<RadiusAuthToken, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}/_radius/_token", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_unix_extend(
|
||||
&self,
|
||||
id: &str,
|
||||
gidnumber: Option<u32>,
|
||||
shell: Option<&str>,
|
||||
) -> Result<bool, ClientError> {
|
||||
let ux = AccountUnixExtend {
|
||||
shell: shell.map(|s| s.to_string()),
|
||||
gidnumber,
|
||||
};
|
||||
self.perform_post_request(format!("/v1/account/{}/_unix", id).as_str(), ux)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -399,13 +1025,114 @@ impl KanidmAsyncClient {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_group_add_members(
|
||||
pub async fn idm_account_get_ssh_pubkeys(&self, id: &str) -> Result<Vec<String>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_post_ssh_pubkey(
|
||||
&self,
|
||||
id: &str,
|
||||
members: Vec<&str>,
|
||||
tag: &str,
|
||||
pubkey: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect();
|
||||
self.perform_post_request(["/v1/group/", id, "/_attr/member"].concat().as_str(), m)
|
||||
let sk = (tag.to_string(), pubkey.to_string());
|
||||
self.perform_post_request(format!("/v1/account/{}/_ssh_pubkeys", id).as_str(), sk)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_person_extend(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_post_request(format!("/v1/account/{}/_person/_extend", id).as_str(), ())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_get_ssh_pubkey(
|
||||
&self,
|
||||
id: &str,
|
||||
tag: &str,
|
||||
) -> Result<Option<String>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys/{}", id, tag).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_account_delete_ssh_pubkey(
|
||||
&self,
|
||||
id: &str,
|
||||
tag: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
self.perform_delete_request(format!("/v1/account/{}/_ssh_pubkeys/{}", id, tag).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
// ==== domain_info (aka domain)
|
||||
pub async fn idm_domain_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/domain").await
|
||||
}
|
||||
|
||||
pub async fn idm_domain_get(&self, id: &str) -> Result<Entry, ClientError> {
|
||||
self.perform_get_request(format!("/v1/domain/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
// pub fn idm_domain_get_attr
|
||||
pub async fn idm_domain_get_ssid(&self, id: &str) -> Result<String, ClientError> {
|
||||
self.perform_get_request(format!("/v1/domain/{}/_attr/domain_ssid", id).as_str())
|
||||
.await
|
||||
.and_then(|mut r: Vec<String>|
|
||||
// Get the first result
|
||||
r.pop()
|
||||
.ok_or(
|
||||
ClientError::EmptyResponse
|
||||
))
|
||||
}
|
||||
|
||||
// pub fn idm_domain_put_attr
|
||||
pub async fn idm_domain_set_ssid(&self, id: &str, ssid: &str) -> Result<bool, ClientError> {
|
||||
self.perform_put_request(
|
||||
format!("/v1/domain/{}/_attr/domain_ssid", id).as_str(),
|
||||
vec![ssid.to_string()],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// ==== schema
|
||||
pub async fn idm_schema_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/schema").await
|
||||
}
|
||||
|
||||
pub async fn idm_schema_attributetype_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/schema/attributetype").await
|
||||
}
|
||||
|
||||
pub async fn idm_schema_attributetype_get(
|
||||
&self,
|
||||
id: &str,
|
||||
) -> Result<Option<Entry>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/schema/attributetype/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn idm_schema_classtype_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/schema/classtype").await
|
||||
}
|
||||
|
||||
pub async fn idm_schema_classtype_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/schema/classtype/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
// ==== recycle bin
|
||||
pub async fn recycle_bin_list(&self) -> Result<Vec<Entry>, ClientError> {
|
||||
self.perform_get_request("/v1/recycle_bin").await
|
||||
}
|
||||
|
||||
pub async fn recycle_bin_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
|
||||
self.perform_get_request(format!("/v1/recycle_bin/{}", id).as_str())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn recycle_bin_revive(&self, id: &str) -> Result<bool, ClientError> {
|
||||
self.perform_post_request(format!("/v1/recycle_bin/{}/_revive", id).as_str(), ())
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -51,6 +51,9 @@ def _authenticate(s, acct, pw):
|
|||
print(r.json())
|
||||
raise Exception("AuthInitFailed")
|
||||
|
||||
session_id = r.headers["x-kanidm-auth-session-id"]
|
||||
headers = {"X-KANIDM-AUTH-SESSION-ID": session_id}
|
||||
|
||||
# {'sessionid': '00000000-5fe5-46e1-06b6-b830dd035a10', 'state': {'choose': ['password']}}
|
||||
if 'password' not in r.json().get('state', {'choose': None}).get('choose', None):
|
||||
print("invalid auth mech presented %s" % r.json())
|
||||
|
@ -58,13 +61,13 @@ def _authenticate(s, acct, pw):
|
|||
|
||||
begin_auth = {"step": {"begin": "password"}}
|
||||
|
||||
r = s.post(AUTH_URL, json=begin_auth, verify=CA, timeout=TIMEOUT)
|
||||
r = s.post(AUTH_URL, json=begin_auth, verify=CA, timeout=TIMEOUT, headers=headers)
|
||||
if r.status_code != 200:
|
||||
print(r.json())
|
||||
raise Exception("AuthBeginFailed")
|
||||
|
||||
cred_auth = {"step": { "cred": {"password": pw}}}
|
||||
r = s.post(AUTH_URL, json=cred_auth, verify=CA, timeout=TIMEOUT)
|
||||
r = s.post(AUTH_URL, json=cred_auth, verify=CA, timeout=TIMEOUT, headers=headers)
|
||||
response = r.json()
|
||||
if r.status_code != 200:
|
||||
print(response)
|
||||
|
|
|
@ -526,7 +526,7 @@ fn test_cache_account_pam_allowed() {
|
|||
.await
|
||||
.expect("failed to auth as admin");
|
||||
adminclient
|
||||
.idm_group_add_members("allowed_group", vec!["testaccount1"])
|
||||
.idm_group_add_members("allowed_group", &["testaccount1"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -4,5 +4,5 @@ db_path = "/tmp/kanidm.db"
|
|||
db_fs_type = "zfs"
|
||||
tls_chain = "../insecure/chain.pem"
|
||||
tls_key = "../insecure/key.pem"
|
||||
log_level = "quiet"
|
||||
log_level = "verbose"
|
||||
origin = "https://idm.example.com"
|
||||
|
|
|
@ -1085,15 +1085,17 @@ impl QueryServerWriteV1 {
|
|||
"class".into(),
|
||||
Value::new_class("posixaccount"),
|
||||
)))
|
||||
.chain(iter::once(gidnumber.as_ref().map(|_| {
|
||||
Modify::Purged("gidnumber".into())
|
||||
})))
|
||||
.chain(iter::once(
|
||||
gidnumber
|
||||
.as_ref()
|
||||
.map(|_| Modify::Purged("gidnumber".into())),
|
||||
))
|
||||
.chain(iter::once(gidnumber.map(|n| {
|
||||
Modify::Present("gidnumber".into(), Value::new_uint32(n))
|
||||
})))
|
||||
.chain(iter::once(shell.as_ref().map(|_| {
|
||||
Modify::Purged("loginshell".into())
|
||||
})))
|
||||
.chain(iter::once(
|
||||
shell.as_ref().map(|_| Modify::Purged("loginshell".into())),
|
||||
))
|
||||
.chain(iter::once(shell.map(|s| {
|
||||
Modify::Present("loginshell".into(), Value::new_iutf8(s.as_str()))
|
||||
})))
|
||||
|
|
|
@ -456,7 +456,7 @@ impl IdlSqliteTransaction for IdlSqliteReadTransaction {
|
|||
|
||||
impl Drop for IdlSqliteReadTransaction {
|
||||
// Abort - so far this has proven reliable to use drop here.
|
||||
fn drop(self: &mut Self) {
|
||||
fn drop(&mut self) {
|
||||
if !self.committed {
|
||||
#[allow(clippy::expect_used)]
|
||||
self.conn
|
||||
|
@ -496,7 +496,7 @@ impl IdlSqliteTransaction for IdlSqliteWriteTransaction {
|
|||
|
||||
impl Drop for IdlSqliteWriteTransaction {
|
||||
// Abort
|
||||
fn drop(self: &mut Self) {
|
||||
fn drop(&mut self) {
|
||||
if !self.committed {
|
||||
#[allow(clippy::expect_used)]
|
||||
self.conn
|
||||
|
|
|
@ -53,6 +53,8 @@ pub struct AppState {
|
|||
pub trait RequestExtensions {
|
||||
fn get_current_uat(&self) -> Option<UserAuthToken>;
|
||||
|
||||
fn get_current_auth_session_id(&self) -> Option<Uuid>;
|
||||
|
||||
fn get_url_param(&self, param: &str) -> Result<String, tide::Error>;
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,27 @@ impl RequestExtensions for tide::Request<AppState> {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_current_auth_session_id(&self) -> Option<Uuid> {
|
||||
// We see if there is a signed header copy first.
|
||||
let kref = &self.state().fernet_handle;
|
||||
self.header("X-KANIDM-AUTH-SESSION-ID")
|
||||
.and_then(|hv| {
|
||||
// Get the first header value.
|
||||
hv.get(0)
|
||||
})
|
||||
.and_then(|h| {
|
||||
// Take the token str and attempt to decrypt
|
||||
// Attempt to re-inflate a uuid from bytes.
|
||||
let uat: Option<Uuid> = kref
|
||||
.decrypt_with_ttl(h.as_str(), 3600)
|
||||
.ok()
|
||||
.and_then(|b| serde_json::from_slice(&b).ok());
|
||||
uat
|
||||
})
|
||||
// If not there, get from the cookie instead.
|
||||
.or_else(|| self.session().get::<Uuid>("auth-session-id"))
|
||||
}
|
||||
|
||||
fn get_url_param(&self, param: &str) -> Result<String, tide::Error> {
|
||||
self.param(param)
|
||||
.map(|s| s.to_string())
|
||||
|
@ -1008,26 +1031,24 @@ pub async fn do_nothing(_req: tide::Request<AppState>) -> tide::Result {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
// We probably need an extract auth or similar to handle the different
|
||||
// types (cookie, bearer), and to generic this over get/post.
|
||||
|
||||
pub async fn auth(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
// AuthRequest
|
||||
|
||||
// First, deal with some state management.
|
||||
// Do anything here first that's needed like getting the session details
|
||||
// out of the req cookie.
|
||||
|
||||
let (eventid, hvalue) = new_eventid!();
|
||||
let maybe_sessionid = req.session().get::<Uuid>("auth-session-id");
|
||||
|
||||
let maybe_sessionid = req.get_current_auth_session_id();
|
||||
debug!("🍿 {:?}", maybe_sessionid);
|
||||
|
||||
let obj: AuthRequest = req.body_json().await
|
||||
.map_err(|e| {debug!("wat? {:?}", e); e})
|
||||
?;
|
||||
let obj: AuthRequest = req.body_json().await.map_err(|e| {
|
||||
debug!("wat? {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
let auth_msg = AuthMessage::new(obj, maybe_sessionid, eventid);
|
||||
|
||||
let mut auth_session_id_tok = None;
|
||||
|
||||
// We probably need to know if we allocate the cookie, that this is a
|
||||
// new session, and in that case, anything *except* authrequest init is
|
||||
// invalid.
|
||||
|
@ -1054,26 +1075,41 @@ pub async fn auth(mut req: tide::Request<AppState>) -> tide::Result {
|
|||
AuthState::Choose(allowed) => {
|
||||
debug!("🧩 -> AuthState::Choose");
|
||||
let msession = req.session_mut();
|
||||
// Force a new cookie session.
|
||||
// msession.regenerate();
|
||||
|
||||
// Ensure the auth-session-id is set
|
||||
msession.remove("auth-session-id");
|
||||
msession
|
||||
.insert("auth-session-id", sessionid)
|
||||
.map(|_| ProtoAuthState::Choose(allowed))
|
||||
.map_err(|_| OperationError::InvalidSessionState)
|
||||
.and_then(|_| {
|
||||
let kref = &req.state().fernet_handle;
|
||||
// Get the header token ready.
|
||||
serde_json::to_vec(&sessionid)
|
||||
.map(|data| {
|
||||
auth_session_id_tok = Some(kref.encrypt(&data));
|
||||
})
|
||||
.map_err(|_| OperationError::InvalidSessionState)
|
||||
})
|
||||
.map(|_| ProtoAuthState::Choose(allowed))
|
||||
}
|
||||
AuthState::Continue(allowed) => {
|
||||
debug!("🧩 -> AuthState::Continue");
|
||||
let msession = req.session_mut();
|
||||
// Force a new cookie session.
|
||||
// msession.regenerate();
|
||||
// Ensure the auth-session-id is set
|
||||
msession.remove("auth-session-id");
|
||||
msession
|
||||
.insert("auth-session-id", sessionid)
|
||||
.map(|_| ProtoAuthState::Continue(allowed))
|
||||
.map_err(|_| OperationError::InvalidSessionState)
|
||||
.and_then(|_| {
|
||||
let kref = &req.state().fernet_handle;
|
||||
// Get the header token ready.
|
||||
serde_json::to_vec(&sessionid)
|
||||
.map(|data| {
|
||||
auth_session_id_tok = Some(kref.encrypt(&data));
|
||||
})
|
||||
.map_err(|_| OperationError::InvalidSessionState)
|
||||
})
|
||||
.map(|_| ProtoAuthState::Continue(allowed))
|
||||
}
|
||||
AuthState::Success(uat) => {
|
||||
debug!("🧩 -> AuthState::Success");
|
||||
|
@ -1102,7 +1138,14 @@ pub async fn auth(mut req: tide::Request<AppState>) -> tide::Result {
|
|||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
to_tide_response(res, hvalue)
|
||||
to_tide_response(res, hvalue).map(|mut res| {
|
||||
// if the sessionid was injected into our cookie, set it in the
|
||||
// header too.
|
||||
if let Some(tok) = auth_session_id_tok {
|
||||
res.insert_header("X-KANIDM-AUTH-SESSION-ID", tok);
|
||||
}
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn idm_account_set_password(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
|
|
|
@ -189,14 +189,10 @@ pub trait QueryServerTransaction {
|
|||
// the QS wr/ro to the plugin trait. However, there shouldn't be a need for search
|
||||
// plugis, because all data transforms should be in the write path.
|
||||
|
||||
let res = self
|
||||
.get_be_txn()
|
||||
.search(au, lims, &vfr)
|
||||
.map(|r| r)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "backend failure -> {:?}", e);
|
||||
OperationError::Backend
|
||||
})?;
|
||||
let res = self.get_be_txn().search(au, lims, &vfr).map_err(|e| {
|
||||
ladmin_error!(au, "backend failure -> {:?}", e);
|
||||
OperationError::Backend
|
||||
})?;
|
||||
|
||||
// Apply ACP before we let the plugins "have at it".
|
||||
// WARNING; for external searches this is NOT the only
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![recursion_limit="256"]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use yew::prelude::*;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
use anyhow::Error;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use yew::format::{Json, Nothing};
|
||||
use yew::prelude::*;
|
||||
use yew::services::{ConsoleService, StorageService};
|
||||
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
|
||||
use yew::services::{ConsoleService, StorageService};
|
||||
|
||||
use kanidm_proto::v1::{AuthRequest, AuthState, AuthStep};
|
||||
|
||||
|
@ -24,32 +24,24 @@ pub enum LoginAppMsg {
|
|||
impl LoginApp {
|
||||
fn auth_begin(&mut self) {
|
||||
let username_copy = self.username.clone();
|
||||
let callback = self.link.callback(
|
||||
move |response: Response<Json<Result<AuthState, Error>>>| {
|
||||
let (parts, body) = response.into_parts();
|
||||
match body {
|
||||
Json(Ok(state)) => {
|
||||
LoginAppMsg::Next(state)
|
||||
let callback =
|
||||
self.link
|
||||
.callback(move |response: Response<Json<Result<AuthState, Error>>>| {
|
||||
let (parts, body) = response.into_parts();
|
||||
match body {
|
||||
Json(Ok(state)) => LoginAppMsg::Next(state),
|
||||
Json(Err(_)) => LoginAppMsg::DoNothing,
|
||||
}
|
||||
Json(Err(_)) => {
|
||||
LoginAppMsg::DoNothing
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Init(self.username.clone())
|
||||
step: AuthStep::Init(self.username.clone()),
|
||||
};
|
||||
// Setup the auth step::init(username);
|
||||
self.ft = Request::post("/v1/auth")
|
||||
.header("Content-Type", "application/json")
|
||||
.body(Json(&authreq))
|
||||
.map_err(|_| ())
|
||||
.and_then(|request| {
|
||||
FetchService::fetch_binary(request, callback)
|
||||
.map_err(|_| ())
|
||||
|
||||
})
|
||||
.and_then(|request| FetchService::fetch_binary(request, callback).map_err(|_| ()))
|
||||
.map(|ft| Some(ft))
|
||||
.unwrap_or_else(|_e| None);
|
||||
}
|
||||
|
@ -63,9 +55,7 @@ impl Component for LoginApp {
|
|||
ConsoleService::log(format!("create").as_str());
|
||||
|
||||
// First we need to work out what state we are in.
|
||||
let lstorage = StorageService::new(
|
||||
yew::services::storage::Area::Local
|
||||
).unwrap();
|
||||
let lstorage = StorageService::new(yew::services::storage::Area::Local).unwrap();
|
||||
|
||||
// Get any previous sessions?
|
||||
// Are they still valid?
|
||||
|
@ -74,7 +64,7 @@ impl Component for LoginApp {
|
|||
link,
|
||||
username: "".to_string(),
|
||||
lstorage,
|
||||
ft: None
|
||||
ft: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +88,7 @@ impl Component for LoginApp {
|
|||
ConsoleService::log(format!("next -> {:?}", state).as_str());
|
||||
true
|
||||
}
|
||||
LoginAppMsg::DoNothing => {
|
||||
false
|
||||
}
|
||||
LoginAppMsg::DoNothing => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue