mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
1116 UI hints (#1199)
This commit is contained in:
parent
33b8fe0967
commit
3589376525
83
Cargo.lock
generated
83
Cargo.lock
generated
|
@ -166,7 +166,7 @@ version = "1.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 1.2.4",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
]
|
||||
|
@ -196,15 +196,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.4.1"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
|
||||
checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 2.0.0",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
"slab",
|
||||
]
|
||||
|
||||
|
@ -248,7 +248,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
|
|||
dependencies = [
|
||||
"async-lock",
|
||||
"autocfg",
|
||||
"concurrent-queue",
|
||||
"concurrent-queue 1.2.4",
|
||||
"futures-lite",
|
||||
"libc",
|
||||
"log",
|
||||
|
@ -626,9 +626,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.74"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
|
||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
@ -809,6 +809,15 @@ dependencies = [
|
|||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.2"
|
||||
|
@ -1105,9 +1114,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.80"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
|
||||
checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
|
@ -1117,9 +1126,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.80"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
|
||||
checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
|
@ -1132,15 +1141,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.80"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
|
||||
checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.80"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
|
||||
checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2036,9 +2045,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.22"
|
||||
version = "0.14.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064"
|
||||
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -2060,9 +2069,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
|
||||
checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
|
@ -2290,6 +2299,7 @@ dependencies = [
|
|||
"base32",
|
||||
"base64urlsafedata",
|
||||
"last-git-commit",
|
||||
"num_enum",
|
||||
"scim_proto",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -2543,7 +2553,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ldap3_client"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/kanidm/ldap3.git#6b0d146d3f85a32add3bdc3639ba4146822eb861"
|
||||
source = "git+https://github.com/kanidm/ldap3.git#1adbd017d02a60e09967572706cb22b1394d927b"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64urlsafedata",
|
||||
|
@ -2562,7 +2572,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ldap3_proto"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/kanidm/ldap3.git#6b0d146d3f85a32add3bdc3639ba4146822eb861"
|
||||
source = "git+https://github.com/kanidm/ldap3.git#1adbd017d02a60e09967572706cb22b1394d927b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"lber",
|
||||
|
@ -2959,9 +2969,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oauth2"
|
||||
version = "4.2.3"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d62c436394991641b970a92e23e8eeb4eb9bca74af4f5badc53bcd568daadbd"
|
||||
checksum = "eeaf26a72311c087f8c5ba617c96fac67a5c04f430e716ac8d8ab2de62e23368"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"chrono",
|
||||
|
@ -3091,9 +3101,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.1"
|
||||
version = "6.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
|
||||
checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
|
@ -4799,9 +4809,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
|
||||
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"serde",
|
||||
|
@ -5031,9 +5041,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webauthn-authenticator-rs"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d30dcdffd0c5dfa110246701399efcc09962c1bb565f61a5d7fe995645ff6f21"
|
||||
checksum = "dbc33594bd26aabc90ce6d081d98617f5d16803660187f88912dead3ac2a9784"
|
||||
dependencies = [
|
||||
"authenticator-ctap2-2021",
|
||||
"base64urlsafedata",
|
||||
|
@ -5045,14 +5055,15 @@ dependencies = [
|
|||
"serde_json",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"webauthn-rs-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webauthn-rs"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5984278a28dc397c565fd79ee1aba67b74fa365c4eea489f7258b3f902422f"
|
||||
checksum = "2db00711c712414e93b019c4596315085792215bc2ac2d5872f9e8913b0a6316"
|
||||
dependencies = [
|
||||
"base64urlsafedata",
|
||||
"serde",
|
||||
|
@ -5064,9 +5075,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webauthn-rs-core"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6528b4769d8fbe020e0e8c2e66bab6035982a2e9c3f8dac384120718f4763f4"
|
||||
checksum = "8ef9a989b5cd2c39c52850c4bc36ebc88907a06ceacf7f80e45d78a308944b9d"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"base64urlsafedata",
|
||||
|
@ -5088,9 +5099,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "webauthn-rs-proto"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09e9a265574f8d7b8f8c94c4488bb491a82d7b8e183003a4512d3dceb88efd98"
|
||||
checksum = "585c7662de492733e6ea11e2726270bf34f5d7f8dbd08cd089830fbb3639a229"
|
||||
dependencies = [
|
||||
"base64urlsafedata",
|
||||
"js-sys",
|
||||
|
|
|
@ -158,10 +158,10 @@ wasm-bindgen = "^0.2.81"
|
|||
wasm-bindgen-futures = "^0.4.30"
|
||||
wasm-bindgen-test = "0.3.33"
|
||||
|
||||
webauthn-authenticator-rs = "0.4.7"
|
||||
webauthn-rs = "0.4.7"
|
||||
webauthn-rs-core = "0.4.7"
|
||||
webauthn-rs-proto = "0.4.7"
|
||||
webauthn-authenticator-rs = "0.4.8"
|
||||
webauthn-rs = "0.4.8"
|
||||
webauthn-rs-core = "0.4.8"
|
||||
webauthn-rs-proto = "0.4.8"
|
||||
# webauthn-authenticator-rs = { path = "../webauthn-rs/webauthn-authenticator-rs" }
|
||||
# webauthn-rs = { path = "../webauthn-rs/webauthn-rs" }
|
||||
# webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" }
|
||||
|
|
|
@ -18,6 +18,7 @@ wasm = ["webauthn-rs-proto/wasm"]
|
|||
[dependencies]
|
||||
base32.workspace = true
|
||||
base64urlsafedata.workspace = true
|
||||
num_enum.workspace = true
|
||||
scim_proto.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_enum::TryFromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs_proto::{
|
||||
|
@ -315,10 +317,34 @@ impl fmt::Display for AuthType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[derive(TryFromPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum UiHint {
|
||||
PosixAccount,
|
||||
ExperimentalFeatures = 0,
|
||||
PosixAccount = 1,
|
||||
}
|
||||
|
||||
impl fmt::Display for UiHint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
UiHint::PosixAccount => write!(f, "PosixAccount"),
|
||||
UiHint::ExperimentalFeatures => write!(f, "ExperimentalFeatures"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UiHint {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"PosixAccount" => Ok(UiHint::PosixAccount),
|
||||
"ExperimentalFeatures" => Ok(UiHint::ExperimentalFeatures),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
|
|
@ -139,7 +139,7 @@ fn from_vec_dbval1(attr_val: Vec<DbValueV1>) -> Result<DbValueSetV2, OperationEr
|
|||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::IndexType(s) = dbv {
|
||||
Ok(s)
|
||||
u16::try_from(s).map_err(|_| OperationError::InvalidValueState)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
|
|
|
@ -495,7 +495,7 @@ pub enum DbValueSetV2 {
|
|||
#[serde(rename = "SY")]
|
||||
SyntaxType(Vec<u16>),
|
||||
#[serde(rename = "IN")]
|
||||
IndexType(Vec<usize>),
|
||||
IndexType(Vec<u16>),
|
||||
#[serde(rename = "RF")]
|
||||
Reference(Vec<Uuid>),
|
||||
#[serde(rename = "JF")]
|
||||
|
@ -550,11 +550,13 @@ pub enum DbValueSetV2 {
|
|||
JwsKeyRs256(Vec<Vec<u8>>),
|
||||
#[serde(rename = "AS")]
|
||||
Oauth2Session(Vec<DbValueOauth2Session>),
|
||||
#[serde(rename = "UH")]
|
||||
UiHint(Vec<u16>),
|
||||
}
|
||||
|
||||
impl DbValueSetV2 {
|
||||
pub fn len(&self) -> usize {
|
||||
match &self {
|
||||
match self {
|
||||
DbValueSetV2::Utf8(set) => set.len(),
|
||||
DbValueSetV2::Iutf8(set) => set.len(),
|
||||
DbValueSetV2::Iname(set) => set.len(),
|
||||
|
@ -589,6 +591,7 @@ impl DbValueSetV2 {
|
|||
DbValueSetV2::Oauth2Session(set) => set.len(),
|
||||
DbValueSetV2::JwsKeyEs256(set) => set.len(),
|
||||
DbValueSetV2::JwsKeyRs256(set) => set.len(),
|
||||
DbValueSetV2::UiHint(set) => set.len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::constants::uuids::*;
|
|||
use crate::constants::values::*;
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
use crate::value::Value;
|
||||
use kanidm_proto::v1::UiHint;
|
||||
|
||||
#[cfg(test)]
|
||||
use uuid::{uuid, Uuid};
|
||||
|
@ -477,6 +478,28 @@ pub const JSON_IDM_ALL_ACCOUNTS: &str = r#"{
|
|||
}
|
||||
}"#;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref E_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: EntryInitNew = entry_init!(
|
||||
("class", CLASS_OBJECT.clone()),
|
||||
("class", CLASS_GROUP.clone()),
|
||||
(
|
||||
"name",
|
||||
Value::new_iname("idm_ui_enable_experimental_features")
|
||||
),
|
||||
(
|
||||
"uuid",
|
||||
Value::new_uuid(UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES)
|
||||
),
|
||||
(
|
||||
"description",
|
||||
Value::new_utf8s(
|
||||
"Members of this group will have access to experimental web UI features."
|
||||
)
|
||||
),
|
||||
("grant_ui_hint", Value::UiHint(UiHint::ExperimentalFeatures))
|
||||
);
|
||||
}
|
||||
|
||||
/// This must be the last group to init to include the UUID of the other high priv groups.
|
||||
pub const JSON_IDM_HIGH_PRIVILEGE_V1: &str = r#"{
|
||||
"attrs": {
|
||||
|
|
|
@ -1262,6 +1262,37 @@ pub const JSON_SCHEMA_ATTR_SYNC_COOKIE: &str = r#"{
|
|||
}
|
||||
}"#;
|
||||
|
||||
pub const JSON_SCHEMA_ATTR_GRANT_UI_HINT: &str = r#"{
|
||||
"attrs": {
|
||||
"class": [
|
||||
"object",
|
||||
"system",
|
||||
"attributetype"
|
||||
],
|
||||
"description": [
|
||||
"A ui hint that is granted via membership to a group"
|
||||
],
|
||||
"index": [
|
||||
"EQUALITY"
|
||||
],
|
||||
"unique": [
|
||||
"false"
|
||||
],
|
||||
"multivalue": [
|
||||
"true"
|
||||
],
|
||||
"attributename": [
|
||||
"grant_ui_hint"
|
||||
],
|
||||
"syntax": [
|
||||
"UIHINT"
|
||||
],
|
||||
"uuid": [
|
||||
"00000000-0000-0000-0000-ffff00000119"
|
||||
]
|
||||
}
|
||||
}"#;
|
||||
|
||||
// === classes ===
|
||||
|
||||
pub const JSON_SCHEMA_CLASS_PERSON: &str = r#"
|
||||
|
@ -1337,7 +1368,8 @@ pub const JSON_SCHEMA_CLASS_GROUP: &str = r#"
|
|||
"group"
|
||||
],
|
||||
"systemmay": [
|
||||
"member"
|
||||
"member",
|
||||
"grant_ui_hint"
|
||||
],
|
||||
"systemmust": [
|
||||
"name",
|
||||
|
|
|
@ -55,6 +55,9 @@ pub const UUID_IDM_ALL_ACCOUNTS: Uuid = uuid!("00000000-0000-0000-0000-000000000
|
|||
pub const _UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: Uuid =
|
||||
uuid!("00000000-0000-0000-0000-000000000037");
|
||||
|
||||
pub const UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: Uuid =
|
||||
uuid!("00000000-0000-0000-0000-000000000038");
|
||||
|
||||
//
|
||||
pub const _UUID_IDM_HIGH_PRIVILEGE: Uuid = uuid!("00000000-0000-0000-0000-000000001000");
|
||||
|
||||
|
@ -206,6 +209,7 @@ pub const _UUID_SCHEMA_ATTR_SYNC_TOKEN_SESSION: Uuid =
|
|||
pub const _UUID_SCHEMA_ATTR_SYNC_COOKIE: Uuid = uuid!("00000000-0000-0000-0000-ffff00000116");
|
||||
pub const _UUID_SCHEMA_ATTR_OAUTH2_SESSION: Uuid = uuid!("00000000-0000-0000-0000-ffff00000117");
|
||||
pub const UUID_SCHEMA_ATTR_ACP_RECEIVER_GROUP: Uuid = uuid!("00000000-0000-0000-0000-ffff00000118");
|
||||
pub const _UUID_SCHEMA_ATTR_GRANT_UI_HINT: Uuid = uuid!("00000000-0000-0000-0000-ffff00000119");
|
||||
|
||||
// System and domain infos
|
||||
// I'd like to strongly criticise william of the past for making poor choices about these allocations.
|
||||
|
|
|
@ -39,6 +39,7 @@ lazy_static! {
|
|||
pub static ref CLASS_ACCOUNT: Value = Value::new_class("account");
|
||||
pub static ref CLASS_DOMAIN_INFO: Value = Value::new_class("domain_info");
|
||||
pub static ref CLASS_DYNGROUP: Value = Value::new_class("dyngroup");
|
||||
pub static ref CLASS_GROUP: Value = Value::new_class("group");
|
||||
pub static ref CLASS_MEMBEROF: Value = Value::new_class("memberof");
|
||||
pub static ref CLASS_OBJECT: Value = Value::new_class("object");
|
||||
pub static ref CLASS_RECYCLED: Value = Value::new_class("recycled");
|
||||
|
|
|
@ -33,6 +33,7 @@ use compact_jwt::JwsSigner;
|
|||
use hashbrown::HashMap;
|
||||
use kanidm_proto::v1::{
|
||||
ConsistencyError, Entry as ProtoEntry, Filter as ProtoFilter, OperationError, SchemaError,
|
||||
UiHint,
|
||||
};
|
||||
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
|
||||
use smartstring::alias::String as AttrString;
|
||||
|
@ -1973,6 +1974,12 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
self.attrs.get(attr).and_then(|vs| vs.as_devicekey_map())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Get the set of passkeys on this account, if any are present.
|
||||
pub fn get_ava_uihint(&self, attr: &str) -> Option<&BTreeSet<UiHint>> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.as_uihint_set())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Return a single secret value, if valid to transform this value.
|
||||
pub fn get_ava_single_secret(&self, attr: &str) -> Option<&str> {
|
||||
|
|
|
@ -91,7 +91,13 @@ macro_rules! try_from_entry {
|
|||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut ui_hints = BTreeSet::default();
|
||||
// Provide hints from groups.
|
||||
let mut ui_hints: BTreeSet<_> = groups
|
||||
.iter()
|
||||
.map(|group: &Group| group.ui_hints.iter())
|
||||
.flatten()
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
if $value.attribute_equality("class", &PVCLASS_POSIXACCOUNT) {
|
||||
ui_hints.insert(UiHint::PosixAccount);
|
||||
|
@ -670,9 +676,10 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::constants::JSON_ANONYMOUS_V1;
|
||||
// use crate::entry::{Entry, EntryNew, EntrySealed};
|
||||
// use crate::idm::account::Account;
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::prelude::*;
|
||||
use async_std::task;
|
||||
use kanidm_proto::v1::{AuthType, UiHint};
|
||||
|
||||
#[test]
|
||||
fn test_idm_account_from_anonymous() {
|
||||
|
@ -682,14 +689,94 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_idm_account_from_real() {
|
||||
// For now, nothing, but later, we'll test different types of cred
|
||||
// passing.
|
||||
}
|
||||
fn test_idm_account_ui_hints() {
|
||||
run_idm_test!(|_qs: &QueryServer,
|
||||
idms: &IdmServer,
|
||||
_idms_delayed: &mut IdmServerDelayed| {
|
||||
let ct = duration_from_epoch_now();
|
||||
let mut idms_prox_write = task::block_on(idms.proxy_write(ct.clone()));
|
||||
|
||||
#[test]
|
||||
fn test_idm_account_set_credential() {
|
||||
// Using a real entry, set a credential back to it's entry.
|
||||
// In the end, this boils down to a modify operation on the Value
|
||||
let target_uuid = Uuid::new_v4();
|
||||
|
||||
// Create a user. So far no ui hints.
|
||||
// Create a service account
|
||||
let e = entry_init!(
|
||||
("class", Value::new_class("object")),
|
||||
("class", Value::new_class("account")),
|
||||
("class", Value::new_class("person")),
|
||||
("name", Value::new_iname("testaccount")),
|
||||
("uuid", Value::new_uuid(target_uuid)),
|
||||
("description", Value::new_utf8s("testaccount")),
|
||||
("displayname", Value::new_utf8s("Test Account"))
|
||||
);
|
||||
|
||||
let ce = CreateEvent::new_internal(vec![e]);
|
||||
assert!(idms_prox_write.qs_write.create(&ce).is_ok());
|
||||
|
||||
let account = idms_prox_write
|
||||
.target_to_account(&target_uuid)
|
||||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Passkey, None)
|
||||
.expect("Unable to create uat");
|
||||
|
||||
// Check the ui hints are as expected.
|
||||
assert!(uat.ui_hints.is_empty());
|
||||
|
||||
// Modify the user to be a posix account, ensure they get the hint.
|
||||
let me_posix = unsafe {
|
||||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq("name", PartialValue::new_iname("testaccount"))),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Present(
|
||||
AttrString::from("class"),
|
||||
Value::new_class("posixaccount"),
|
||||
),
|
||||
Modify::Present(AttrString::from("gidnumber"), Value::new_uint32(2001)),
|
||||
]),
|
||||
)
|
||||
};
|
||||
assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
|
||||
|
||||
// Check the ui hints are as expected.
|
||||
let account = idms_prox_write
|
||||
.target_to_account(&target_uuid)
|
||||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Passkey, None)
|
||||
.expect("Unable to create uat");
|
||||
|
||||
assert!(uat.ui_hints.len() == 1);
|
||||
assert!(uat.ui_hints.contains(&UiHint::PosixAccount));
|
||||
|
||||
// Add a group with a ui hint, and then check they get the hint.
|
||||
let e = entry_init!(
|
||||
("class", Value::new_class("object")),
|
||||
("class", Value::new_class("group")),
|
||||
("name", Value::new_iname("test_uihint_group")),
|
||||
("member", Value::Refer(target_uuid)),
|
||||
("grant_ui_hint", Value::UiHint(UiHint::ExperimentalFeatures))
|
||||
);
|
||||
|
||||
let ce = CreateEvent::new_internal(vec![e]);
|
||||
assert!(idms_prox_write.qs_write.create(&ce).is_ok());
|
||||
|
||||
// Check the ui hints are as expected.
|
||||
let account = idms_prox_write
|
||||
.target_to_account(&target_uuid)
|
||||
.expect("account must exist");
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Passkey, None)
|
||||
.expect("Unable to create uat");
|
||||
|
||||
assert!(uat.ui_hints.len() == 2);
|
||||
assert!(uat.ui_hints.contains(&UiHint::PosixAccount));
|
||||
assert!(uat.ui_hints.contains(&UiHint::ExperimentalFeatures));
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use kanidm_proto::v1::UiHint;
|
||||
use kanidm_proto::v1::{Group as ProtoGroup, OperationError};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -10,6 +13,7 @@ pub struct Group {
|
|||
spn: String,
|
||||
uuid: Uuid,
|
||||
// We'll probably add policy and claims later to this
|
||||
pub ui_hints: BTreeSet<UiHint>,
|
||||
}
|
||||
|
||||
macro_rules! try_from_account_e {
|
||||
|
@ -23,13 +27,21 @@ macro_rules! try_from_account_e {
|
|||
})?;
|
||||
*/
|
||||
|
||||
// Setup the user private group
|
||||
let spn = $value.get_ava_single_proto_string("spn").ok_or(
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string()),
|
||||
)?;
|
||||
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let upg = Group { spn, uuid };
|
||||
// We could allow ui hints on the user direct in the future?
|
||||
let ui_hints = BTreeSet::default();
|
||||
|
||||
let upg = Group {
|
||||
spn,
|
||||
uuid,
|
||||
ui_hints,
|
||||
};
|
||||
|
||||
let mut groups: Vec<Group> = match $value.get_ava_as_refuuid("memberof") {
|
||||
Some(riter) => {
|
||||
|
@ -111,7 +123,16 @@ impl Group {
|
|||
|
||||
let uuid = value.get_uuid();
|
||||
|
||||
Ok(Group { spn, uuid })
|
||||
let ui_hints = value
|
||||
.get_ava_uihint("grant_ui_hint")
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Group {
|
||||
spn,
|
||||
uuid,
|
||||
ui_hints,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> ProtoGroup {
|
||||
|
|
|
@ -197,6 +197,7 @@ impl SchemaAttribute {
|
|||
// These are just insensitive string lookups on the hex-ified kid.
|
||||
SyntaxType::JwsKeyEs256 => matches!(v, PartialValue::Iutf8(_)),
|
||||
SyntaxType::JwsKeyRs256 => matches!(v, PartialValue::Iutf8(_)),
|
||||
SyntaxType::UiHint => matches!(v, PartialValue::UiHint(_)),
|
||||
};
|
||||
if r {
|
||||
Ok(())
|
||||
|
@ -243,6 +244,7 @@ impl SchemaAttribute {
|
|||
SyntaxType::Oauth2Session => matches!(v, Value::Oauth2Session(_, _)),
|
||||
SyntaxType::JwsKeyEs256 => matches!(v, Value::JwsKeyEs256(_)),
|
||||
SyntaxType::JwsKeyRs256 => matches!(v, Value::JwsKeyRs256(_)),
|
||||
SyntaxType::UiHint => matches!(v, Value::UiHint(_)),
|
||||
};
|
||||
if r {
|
||||
Ok(())
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
// This is really only used for long lived, high level types that need clone
|
||||
// that otherwise can't be cloned. Think Mutex.
|
||||
use std::cell::Cell;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn};
|
||||
use concread::cowcell::*;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use kanidm_proto::v1::{ConsistencyError, SchemaError};
|
||||
use kanidm_proto::v1::{ConsistencyError, SchemaError, UiHint};
|
||||
use tokio::sync::{Semaphore, SemaphorePermit};
|
||||
use tracing::trace;
|
||||
|
||||
|
@ -517,6 +518,9 @@ pub trait QueryServerTransaction<'a> {
|
|||
SyntaxType::JwsKeyEs256 => Err(OperationError::InvalidAttribute("JwsKeyEs256 Values can not be supplied through modification".to_string())),
|
||||
SyntaxType::JwsKeyRs256 => Err(OperationError::InvalidAttribute("JwsKeyRs256 Values can not be supplied through modification".to_string())),
|
||||
SyntaxType::Oauth2Session => Err(OperationError::InvalidAttribute("Oauth2Session Values can not be supplied through modification".to_string())),
|
||||
SyntaxType::UiHint => UiHint::from_str(value)
|
||||
.map(Value::UiHint)
|
||||
.map_err(|()| OperationError::InvalidAttribute("Invalid uihint syntax".to_string())),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
@ -645,6 +649,11 @@ pub trait QueryServerTransaction<'a> {
|
|||
)
|
||||
})
|
||||
}
|
||||
SyntaxType::UiHint => UiHint::from_str(value)
|
||||
.map(PartialValue::UiHint)
|
||||
.map_err(|()| {
|
||||
OperationError::InvalidAttribute("Invalid uihint syntax".to_string())
|
||||
}),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
@ -2682,6 +2691,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
JSON_SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME,
|
||||
JSON_SCHEMA_ATTR_SYNC_TOKEN_SESSION,
|
||||
JSON_SCHEMA_ATTR_SYNC_COOKIE,
|
||||
JSON_SCHEMA_ATTR_GRANT_UI_HINT,
|
||||
JSON_SCHEMA_CLASS_PERSON,
|
||||
JSON_SCHEMA_CLASS_ORGPERSON,
|
||||
JSON_SCHEMA_CLASS_GROUP,
|
||||
|
@ -2848,13 +2858,26 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
debug_assert!(res.is_ok());
|
||||
res?;
|
||||
|
||||
let idm_entries = [E_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES.clone()];
|
||||
|
||||
let res: Result<(), _> = idm_entries
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry));
|
||||
if res.is_ok() {
|
||||
admin_debug!("initialise_idm -> result Ok!");
|
||||
} else {
|
||||
admin_error!(?res, "initialise_idm p3 -> result");
|
||||
}
|
||||
debug_assert!(res.is_ok());
|
||||
res?;
|
||||
|
||||
self.changed_schema.set(true);
|
||||
self.changed_acp.set(true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "info", name = "reload_schema", skip(self))]
|
||||
#[instrument(level = "debug", name = "reload_schema", skip(self))]
|
||||
fn reload_schema(&mut self) -> Result<(), OperationError> {
|
||||
// supply entries to the writable schema to reload from.
|
||||
// find all attributes.
|
||||
|
|
|
@ -12,6 +12,8 @@ use std::time::Duration;
|
|||
use compact_jwt::JwsSigner;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use kanidm_proto::v1::UiHint;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sshkeys::PublicKey as SshPublicKey;
|
||||
|
@ -72,14 +74,6 @@ pub struct Address {
|
|||
pub country: String,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Hash)]
|
||||
pub enum IndexType {
|
||||
Equality,
|
||||
Presence,
|
||||
SubString,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum IntentTokenState {
|
||||
Valid {
|
||||
|
@ -95,6 +89,27 @@ pub enum IntentTokenState {
|
|||
},
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
Hash,
|
||||
TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u16)]
|
||||
pub enum IndexType {
|
||||
Equality,
|
||||
Presence,
|
||||
SubString,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for IndexType {
|
||||
type Error = ();
|
||||
|
||||
|
@ -111,19 +126,6 @@ impl TryFrom<&str> for IndexType {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for IndexType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(IndexType::Equality),
|
||||
1 => Ok(IndexType::Presence),
|
||||
2 => Ok(IndexType::SubString),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexType {
|
||||
pub fn as_idx_str(&self) -> &str {
|
||||
match self {
|
||||
|
@ -132,14 +134,6 @@ impl IndexType {
|
|||
IndexType::SubString => "sub",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_usize(&self) -> usize {
|
||||
match self {
|
||||
IndexType::Equality => 0,
|
||||
IndexType::Presence => 1,
|
||||
IndexType::SubString => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IndexType {
|
||||
|
@ -157,7 +151,19 @@ impl fmt::Display for IndexType {
|
|||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[derive(
|
||||
Hash,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
TryFromPrimitive,
|
||||
)]
|
||||
#[repr(u16)]
|
||||
pub enum SyntaxType {
|
||||
Utf8String = 0,
|
||||
|
@ -189,6 +195,7 @@ pub enum SyntaxType {
|
|||
JwsKeyEs256 = 26,
|
||||
JwsKeyRs256 = 27,
|
||||
Oauth2Session = 28,
|
||||
UiHint = 29,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for SyntaxType {
|
||||
|
@ -227,45 +234,7 @@ impl TryFrom<&str> for SyntaxType {
|
|||
"JWS_KEY_ES256" => Ok(SyntaxType::JwsKeyEs256),
|
||||
"JWS_KEY_RS256" => Ok(SyntaxType::JwsKeyRs256),
|
||||
"OAUTH2SESSION" => Ok(SyntaxType::Oauth2Session),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for SyntaxType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(SyntaxType::Utf8String),
|
||||
1 => Ok(SyntaxType::Utf8StringInsensitive),
|
||||
2 => Ok(SyntaxType::Uuid),
|
||||
3 => Ok(SyntaxType::Boolean),
|
||||
4 => Ok(SyntaxType::SyntaxId),
|
||||
5 => Ok(SyntaxType::IndexId),
|
||||
6 => Ok(SyntaxType::ReferenceUuid),
|
||||
7 => Ok(SyntaxType::JsonFilter),
|
||||
8 => Ok(SyntaxType::Credential),
|
||||
9 => Ok(SyntaxType::SecretUtf8String),
|
||||
10 => Ok(SyntaxType::SshKey),
|
||||
11 => Ok(SyntaxType::SecurityPrincipalName),
|
||||
12 => Ok(SyntaxType::Uint32),
|
||||
13 => Ok(SyntaxType::Cid),
|
||||
14 => Ok(SyntaxType::Utf8StringIname),
|
||||
15 => Ok(SyntaxType::NsUniqueId),
|
||||
16 => Ok(SyntaxType::DateTime),
|
||||
17 => Ok(SyntaxType::EmailAddress),
|
||||
18 => Ok(SyntaxType::Url),
|
||||
19 => Ok(SyntaxType::OauthScope),
|
||||
20 => Ok(SyntaxType::OauthScopeMap),
|
||||
21 => Ok(SyntaxType::PrivateBinary),
|
||||
22 => Ok(SyntaxType::IntentToken),
|
||||
23 => Ok(SyntaxType::Passkey),
|
||||
24 => Ok(SyntaxType::DeviceKey),
|
||||
25 => Ok(SyntaxType::Session),
|
||||
26 => Ok(SyntaxType::JwsKeyEs256),
|
||||
27 => Ok(SyntaxType::JwsKeyRs256),
|
||||
28 => Ok(SyntaxType::Oauth2Session),
|
||||
"UIHINT" => Ok(SyntaxType::UiHint),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -303,6 +272,7 @@ impl fmt::Display for SyntaxType {
|
|||
SyntaxType::JwsKeyEs256 => "JWS_KEY_ES256",
|
||||
SyntaxType::JwsKeyRs256 => "JWS_KEY_RS256",
|
||||
SyntaxType::Oauth2Session => "OAUTH2SESSION",
|
||||
SyntaxType::UiHint => "UIHINT",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -348,6 +318,7 @@ pub enum PartialValue {
|
|||
// Float64(f64),
|
||||
RestrictedString(String),
|
||||
IntentToken(String),
|
||||
UiHint(UiHint),
|
||||
|
||||
Passkey(Uuid),
|
||||
DeviceKey(Uuid),
|
||||
|
@ -697,7 +668,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn get_idx_eq_key(&self) -> String {
|
||||
match &self {
|
||||
match self {
|
||||
PartialValue::Utf8(s)
|
||||
| PartialValue::Iutf8(s)
|
||||
| PartialValue::Iname(s)
|
||||
|
@ -736,6 +707,7 @@ impl PartialValue {
|
|||
PartialValue::IntentToken(u) => u.clone(),
|
||||
PartialValue::TrustedDeviceEnrollment(u) => u.as_hyphenated().to_string(),
|
||||
PartialValue::Session(u) => u.as_hyphenated().to_string(),
|
||||
PartialValue::UiHint(u) => (*u as u16).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,6 +782,7 @@ pub enum Value {
|
|||
|
||||
JwsKeyEs256(JwsSigner),
|
||||
JwsKeyRs256(JwsSigner),
|
||||
UiHint(UiHint),
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
|
|
|
@ -20,9 +20,9 @@ impl ValueSetIndex {
|
|||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<usize>) -> Result<ValueSet, OperationError> {
|
||||
pub fn from_dbvs2(data: Vec<u16>) -> Result<ValueSet, OperationError> {
|
||||
let set: Result<_, _> = data.into_iter().map(IndexType::try_from).collect();
|
||||
let set = set.map_err(|()| OperationError::InvalidValueState)?;
|
||||
let set = set.map_err(|_| OperationError::InvalidValueState)?;
|
||||
Ok(Box::new(ValueSetIndex { set }))
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ impl ValueSetT for ValueSetIndex {
|
|||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::IndexType(self.set.iter().map(|s| s.to_usize()).collect())
|
||||
DbValueSetV2::IndexType(self.set.iter().map(|s| *s as u16).collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
|
|
|
@ -4,6 +4,7 @@ use compact_jwt::JwsSigner;
|
|||
use dyn_clone::DynClone;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use kanidm_proto::v1::UiHint;
|
||||
use smolset::SmolSet;
|
||||
use time::OffsetDateTime;
|
||||
// use std::fmt::Debug;
|
||||
|
@ -36,6 +37,7 @@ mod session;
|
|||
mod spn;
|
||||
mod ssh;
|
||||
mod syntax;
|
||||
mod uihint;
|
||||
mod uint32;
|
||||
mod url;
|
||||
mod utf8;
|
||||
|
@ -60,6 +62,7 @@ pub use self::session::{ValueSetOauth2Session, ValueSetSession};
|
|||
pub use self::spn::ValueSetSpn;
|
||||
pub use self::ssh::ValueSetSshKey;
|
||||
pub use self::syntax::ValueSetSyntax;
|
||||
pub use self::uihint::ValueSetUiHint;
|
||||
pub use self::uint32::ValueSetUint32;
|
||||
pub use self::url::ValueSetUrl;
|
||||
pub use self::utf8::ValueSetUtf8;
|
||||
|
@ -495,6 +498,16 @@ pub trait ValueSetT: std::fmt::Debug + DynClone {
|
|||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_uihint_set(&self) -> Option<&BTreeSet<UiHint>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_uihint_iter(&self) -> Option<Box<dyn Iterator<Item = UiHint> + '_>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ValueSet {
|
||||
|
@ -546,6 +559,7 @@ pub fn from_result_value_iter(
|
|||
Value::PublicBinary(t, b) => ValueSetPublicBinary::new(t, b),
|
||||
Value::IntentToken(u, s) => ValueSetIntentToken::new(u, s),
|
||||
Value::EmailAddress(a, _) => ValueSetEmailAddress::new(a),
|
||||
Value::UiHint(u) => ValueSetUiHint::new(u),
|
||||
Value::PhoneNumber(_, _)
|
||||
| Value::Passkey(_, _, _)
|
||||
| Value::DeviceKey(_, _, _)
|
||||
|
@ -608,6 +622,7 @@ pub fn from_value_iter(mut iter: impl Iterator<Item = Value>) -> Result<ValueSet
|
|||
Value::JwsKeyRs256(k) => ValueSetJwsKeyRs256::new(k),
|
||||
Value::Session(u, m) => ValueSetSession::new(u, m),
|
||||
Value::Oauth2Session(u, m) => ValueSetOauth2Session::new(u, m),
|
||||
Value::UiHint(u) => ValueSetUiHint::new(u),
|
||||
Value::PhoneNumber(_, _) | Value::TrustedDeviceEnrollment(_) => {
|
||||
debug_assert!(false);
|
||||
return Err(OperationError::InvalidValueState);
|
||||
|
@ -654,6 +669,7 @@ pub fn from_db_valueset_v2(dbvs: DbValueSetV2) -> Result<ValueSet, OperationErro
|
|||
DbValueSetV2::Oauth2Session(set) => ValueSetOauth2Session::from_dbvs2(set),
|
||||
DbValueSetV2::JwsKeyEs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set),
|
||||
DbValueSetV2::JwsKeyRs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set),
|
||||
DbValueSetV2::UiHint(set) => ValueSetUiHint::from_dbvs2(set),
|
||||
DbValueSetV2::PhoneNumber(_, _) | DbValueSetV2::TrustedDeviceEnrollment(_) => {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ impl ValueSetSyntax {
|
|||
|
||||
pub fn from_dbvs2(data: Vec<u16>) -> Result<ValueSet, OperationError> {
|
||||
let set: Result<_, _> = data.into_iter().map(SyntaxType::try_from).collect();
|
||||
let set = set.map_err(|()| OperationError::InvalidValueState)?;
|
||||
let set = set.map_err(|_| OperationError::InvalidValueState)?;
|
||||
Ok(Box::new(ValueSetSyntax { set }))
|
||||
}
|
||||
}
|
||||
|
|
126
kanidmd/lib/src/valueset/uihint.rs
Normal file
126
kanidmd/lib/src/valueset/uihint.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::{DbValueSetV2, ValueSet};
|
||||
|
||||
use kanidm_proto::v1::UiHint;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetUiHint {
|
||||
set: BTreeSet<UiHint>,
|
||||
}
|
||||
|
||||
impl ValueSetUiHint {
|
||||
pub fn new(s: UiHint) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetUiHint { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: UiHint) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<u16>) -> Result<ValueSet, OperationError> {
|
||||
let set: Result<_, _> = data.into_iter().map(UiHint::try_from).collect();
|
||||
let set = set.map_err(|_| OperationError::InvalidValueState)?;
|
||||
Ok(Box::new(ValueSetUiHint { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetUiHint {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::UiHint(s) => Ok(self.set.insert(s)),
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::UiHint(s) => self.set.remove(s),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::UiHint(s) => self.set.contains(&s),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|u| (*u as u16).to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::UiHint
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|u| u.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::UiHint(self.set.iter().map(|u| *u as u16).collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|i| PartialValue::UiHint(i)))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|i| Value::UiHint(i)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_uihint_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_uihint_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uihint_set(&self) -> Option<&BTreeSet<UiHint>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_uihint_iter(&self) -> Option<Box<dyn Iterator<Item = UiHint> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
}
|
|
@ -233,7 +233,7 @@ function addBorrowedObject(obj) {
|
|||
}
|
||||
function __wbg_adapter_48(arg0, arg1, arg2) {
|
||||
try {
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha86bc8783d36be0a(arg0, arg1, addBorrowedObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h2f44da981a3bd620(arg0, arg1, addBorrowedObject(arg2));
|
||||
} finally {
|
||||
heap[stack_pointer++] = undefined;
|
||||
}
|
||||
|
@ -261,11 +261,11 @@ function makeClosure(arg0, arg1, dtor, f) {
|
|||
return real;
|
||||
}
|
||||
function __wbg_adapter_51(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h36bbc8108d49feb4(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h10a3687bc3b2a4bb(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function __wbg_adapter_54(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc4ba360e62a5fa4a(arg0, arg1, addHeapObject(arg2));
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6d8a3152557e4ad6(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -345,25 +345,12 @@ async function load(module, imports) {
|
|||
function getImports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_in = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) in getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
|
@ -372,23 +359,14 @@ function getImports() {
|
|||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_bigint = function(arg0) {
|
||||
const ret = typeof(getObject(arg0)) === 'bigint';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_string = function(arg0) {
|
||||
const ret = typeof(getObject(arg0)) === 'string';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_bigint_from_i64 = function(arg0) {
|
||||
const ret = arg0;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_jsval_eq = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) === getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
|
||||
const ret = BigInt.asUintN(64, arg0);
|
||||
return addHeapObject(ret);
|
||||
|
@ -405,13 +383,35 @@ function getImports() {
|
|||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_jsval_eq = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) === getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_in = function(arg0, arg1) {
|
||||
const ret = getObject(arg0) in getObject(arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = getObject(arg0);
|
||||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_bigint = function(arg0) {
|
||||
const ret = typeof(getObject(arg0)) === 'bigint';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_string = function(arg0) {
|
||||
const ret = typeof(getObject(arg0)) === 'string';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
|
@ -421,7 +421,7 @@ function getImports() {
|
|||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_modalhidebyid_4a1a18ce4b8f3393 = function(arg0, arg1) {
|
||||
imports.wbg.__wbg_modalhidebyid_130cc6453fa7b55b = function(arg0, arg1) {
|
||||
modal_hide_by_id(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_number_new = function(arg0) {
|
||||
|
@ -539,6 +539,43 @@ function getImports() {
|
|||
const ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_log_4b5638ad60bdc54a = function(arg0) {
|
||||
console.log(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_value_ccb32485ee1b3928 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).value;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_df64bc6794c098f2 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_new_2d0053ee81e4dd2a = function() { return handleError(function () {
|
||||
const ret = new Headers();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_31b57952dfc2c6cc = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_992c1d31586b2957 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlFormElement_1c489ff7e99e43d3 = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = getObject(arg0) instanceof HTMLFormElement;
|
||||
} catch {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_getItem_845e475f85f593e4 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -552,6 +589,16 @@ function getImports() {
|
|||
imports.wbg.__wbg_setItem_9c469d634d0c321c = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_ca4d3a3eca340210 = function() { return handleError(function () {
|
||||
const ret = new URLSearchParams();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_add_89a4f3b0846cf0aa = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_remove_1a26eb5d822902ed = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_pathname_78a642e573bf8169 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).pathname;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
|
@ -573,69 +620,6 @@ function getImports() {
|
|||
const ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_ca4d3a3eca340210 = function() { return handleError(function () {
|
||||
const ret = new URLSearchParams();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_2d0053ee81e4dd2a = function() { return handleError(function () {
|
||||
const ret = new Headers();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_31b57952dfc2c6cc = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = getObject(arg1).get(getStringFromWasm0(arg2, arg3));
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_992c1d31586b2957 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_value_ccb32485ee1b3928 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).value;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_df64bc6794c098f2 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_url_1c013f0875e97715 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).url;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_headers_85824e993aa739bf = function(arg0) {
|
||||
const ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithstr_fdce36db91ec5f92 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_add_89a4f3b0846cf0aa = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_remove_1a26eb5d822902ed = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_HtmlFormElement_1c489ff7e99e43d3 = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
result = getObject(arg0) instanceof HTMLFormElement;
|
||||
} catch {
|
||||
result = false;
|
||||
}
|
||||
const ret = result;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_HtmlInputElement_970e4026de0fccff = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
|
@ -659,6 +643,40 @@ function getImports() {
|
|||
imports.wbg.__wbg_setvalue_e5b519cca37d82a7 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_create_53c6ddb068a22172 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).create(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_da97585bbb5a63bb = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_90ff36b5040e3b76 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).href;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_url_1c013f0875e97715 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).url;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_headers_85824e993aa739bf = function(arg0) {
|
||||
const ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithstr_fdce36db91ec5f92 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithstrandinit_05d7180788420c40 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_Element_33bd126d58f2021b = function(arg0) {
|
||||
let result;
|
||||
try {
|
||||
|
@ -702,21 +720,6 @@ function getImports() {
|
|||
imports.wbg.__wbg_focus_adfe4cc61e2c09bc = function() { return handleError(function (arg0) {
|
||||
getObject(arg0).focus();
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_create_53c6ddb068a22172 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).create(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_da97585bbb5a63bb = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).get(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_href_90ff36b5040e3b76 = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).href;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_credentials_eab5c0bffc3e9cc5 = function(arg0) {
|
||||
const ret = getObject(arg0).credentials;
|
||||
return addHeapObject(ret);
|
||||
|
@ -746,12 +749,6 @@ function getImports() {
|
|||
imports.wbg.__wbg_preventDefault_3209279b490de583 = function(arg0) {
|
||||
getObject(arg0).preventDefault();
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_1fc744729ac6dc27 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_b10f1a66647f3aa0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newwithform_6b545e9ddaccc455 = function() { return handleError(function (arg0) {
|
||||
const ret = new FormData(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
|
@ -760,6 +757,12 @@ function getImports() {
|
|||
const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_1fc744729ac6dc27 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeEventListener_b10f1a66647f3aa0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_parentElement_0cffb3ceb0f107bd = function(arg0) {
|
||||
const ret = getObject(arg0).parentElement;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
|
@ -1037,16 +1040,16 @@ function getImports() {
|
|||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper5651 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1343, __wbg_adapter_48);
|
||||
imports.wbg.__wbindgen_closure_wrapper4757 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1013, __wbg_adapter_48);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper5814 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1378, __wbg_adapter_51);
|
||||
imports.wbg.__wbindgen_closure_wrapper4941 = function(arg0, arg1, arg2) {
|
||||
const ret = makeClosure(arg0, arg1, 1037, __wbg_adapter_51);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper6542 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1635, __wbg_adapter_54);
|
||||
imports.wbg.__wbindgen_closure_wrapper5597 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1287, __wbg_adapter_54);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
|
Binary file not shown.
|
@ -5,7 +5,7 @@
|
|||
"James Hodgkinson <james@terminaloutcomes.com>"
|
||||
],
|
||||
"description": "Kanidm Server Web User Interface",
|
||||
"version": "1.1.0-alpha.10",
|
||||
"version": "1.1.0-alpha.11-dev",
|
||||
"license": "MPL-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use gloo::console;
|
||||
use kanidm_proto::v1::UserAuthToken;
|
||||
use kanidm_proto::v1::{UserAuthToken, UiHint};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
@ -236,6 +236,8 @@ impl ViewsApp {
|
|||
fn view_authenticated(&self, ctx: &Context<Self>, uat: &UserAuthToken) -> Html {
|
||||
let current_user_uat = uat.clone();
|
||||
|
||||
let ui_hint_experimental = uat.ui_hints.contains(&UiHint::ExperimentalFeatures);
|
||||
|
||||
// WARN set dash-body against body here?
|
||||
html! {
|
||||
<>
|
||||
|
@ -247,41 +249,48 @@ impl ViewsApp {
|
|||
<button class="navbar-toggler bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<img src="/pkg/img/favicon.png" />
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Apps}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Apps" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Profile}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Profile" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Apps}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Apps" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Security}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Security" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
// TODO: the admin link should only show up if you're an admin
|
||||
<li class="mb-1">
|
||||
<Link<AdminRoute> classes="nav-link" to={AdminRoute::AdminMenu}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Admin" }
|
||||
</Link<AdminRoute>>
|
||||
</li>
|
||||
<li class="mb-1">
|
||||
<a class="nav-link" href="#"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={format!("#{}", crate::constants::ID_SIGNOUTMODAL)}
|
||||
>{"Sign out"}</a>
|
||||
</li>
|
||||
if ui_hint_experimental {
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Profile}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Profile" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
}
|
||||
|
||||
<li class="mb-1">
|
||||
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Security}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Security" }
|
||||
</Link<ViewRoute>>
|
||||
</li>
|
||||
|
||||
if ui_hint_experimental {
|
||||
<li class="mb-1">
|
||||
<Link<AdminRoute> classes="nav-link" to={AdminRoute::AdminMenu}>
|
||||
<span data-feather="file"></span>
|
||||
{ "Admin" }
|
||||
</Link<AdminRoute>>
|
||||
</li>
|
||||
}
|
||||
|
||||
<li class="mb-1">
|
||||
<a class="nav-link" href="#"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={format!("#{}", crate::constants::ID_SIGNOUTMODAL)}
|
||||
>{"Sign out"}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" />
|
||||
|
|
Loading…
Reference in a new issue