1116 UI hints (#1199)

This commit is contained in:
Firstyear 2022-11-16 19:17:24 +10:00 committed by GitHub
parent 33b8fe0967
commit 3589376525
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 661 additions and 293 deletions

83
Cargo.lock generated
View file

@ -166,7 +166,7 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
dependencies = [ dependencies = [
"concurrent-queue", "concurrent-queue 1.2.4",
"event-listener", "event-listener",
"futures-core", "futures-core",
] ]
@ -196,15 +196,15 @@ dependencies = [
[[package]] [[package]]
name = "async-executor" name = "async-executor"
version = "1.4.1" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
dependencies = [ dependencies = [
"async-lock",
"async-task", "async-task",
"concurrent-queue", "concurrent-queue 2.0.0",
"fastrand", "fastrand",
"futures-lite", "futures-lite",
"once_cell",
"slab", "slab",
] ]
@ -248,7 +248,7 @@ checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
dependencies = [ dependencies = [
"async-lock", "async-lock",
"autocfg", "autocfg",
"concurrent-queue", "concurrent-queue 1.2.4",
"futures-lite", "futures-lite",
"libc", "libc",
"log", "log",
@ -626,9 +626,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.74" version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
dependencies = [ dependencies = [
"jobserver", "jobserver",
] ]
@ -809,6 +809,15 @@ dependencies = [
"cache-padded", "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]] [[package]]
name = "console" name = "console"
version = "0.15.2" version = "0.15.2"
@ -1105,9 +1114,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.80" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
dependencies = [ dependencies = [
"cc", "cc",
"cxxbridge-flags", "cxxbridge-flags",
@ -1117,9 +1126,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx-build" name = "cxx-build"
version = "1.0.80" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
dependencies = [ dependencies = [
"cc", "cc",
"codespan-reporting", "codespan-reporting",
@ -1132,15 +1141,15 @@ dependencies = [
[[package]] [[package]]
name = "cxxbridge-flags" name = "cxxbridge-flags"
version = "1.0.80" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
[[package]] [[package]]
name = "cxxbridge-macro" name = "cxxbridge-macro"
version = "1.0.80" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2036,9 +2045,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.22" version = "0.14.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -2060,9 +2069,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-rustls" name = "hyper-rustls"
version = "0.23.0" version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d"
dependencies = [ dependencies = [
"http", "http",
"hyper", "hyper",
@ -2290,6 +2299,7 @@ dependencies = [
"base32", "base32",
"base64urlsafedata", "base64urlsafedata",
"last-git-commit", "last-git-commit",
"num_enum",
"scim_proto", "scim_proto",
"serde", "serde",
"serde_json", "serde_json",
@ -2543,7 +2553,7 @@ dependencies = [
[[package]] [[package]]
name = "ldap3_client" name = "ldap3_client"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/kanidm/ldap3.git#6b0d146d3f85a32add3bdc3639ba4146822eb861" source = "git+https://github.com/kanidm/ldap3.git#1adbd017d02a60e09967572706cb22b1394d927b"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"base64urlsafedata", "base64urlsafedata",
@ -2562,7 +2572,7 @@ dependencies = [
[[package]] [[package]]
name = "ldap3_proto" name = "ldap3_proto"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/kanidm/ldap3.git#6b0d146d3f85a32add3bdc3639ba4146822eb861" source = "git+https://github.com/kanidm/ldap3.git#1adbd017d02a60e09967572706cb22b1394d927b"
dependencies = [ dependencies = [
"bytes", "bytes",
"lber", "lber",
@ -2959,9 +2969,9 @@ dependencies = [
[[package]] [[package]]
name = "oauth2" name = "oauth2"
version = "4.2.3" version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d62c436394991641b970a92e23e8eeb4eb9bca74af4f5badc53bcd568daadbd" checksum = "eeaf26a72311c087f8c5ba617c96fac67a5c04f430e716ac8d8ab2de62e23368"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"chrono", "chrono",
@ -3091,9 +3101,9 @@ dependencies = [
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.3.1" version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
[[package]] [[package]]
name = "overload" name = "overload"
@ -4799,9 +4809,9 @@ dependencies = [
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.2.1" version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [ dependencies = [
"getrandom 0.2.8", "getrandom 0.2.8",
"serde", "serde",
@ -5031,9 +5041,9 @@ dependencies = [
[[package]] [[package]]
name = "webauthn-authenticator-rs" name = "webauthn-authenticator-rs"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d30dcdffd0c5dfa110246701399efcc09962c1bb565f61a5d7fe995645ff6f21" checksum = "dbc33594bd26aabc90ce6d081d98617f5d16803660187f88912dead3ac2a9784"
dependencies = [ dependencies = [
"authenticator-ctap2-2021", "authenticator-ctap2-2021",
"base64urlsafedata", "base64urlsafedata",
@ -5045,14 +5055,15 @@ dependencies = [
"serde_json", "serde_json",
"tracing", "tracing",
"url", "url",
"uuid",
"webauthn-rs-proto", "webauthn-rs-proto",
] ]
[[package]] [[package]]
name = "webauthn-rs" name = "webauthn-rs"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5984278a28dc397c565fd79ee1aba67b74fa365c4eea489f7258b3f902422f" checksum = "2db00711c712414e93b019c4596315085792215bc2ac2d5872f9e8913b0a6316"
dependencies = [ dependencies = [
"base64urlsafedata", "base64urlsafedata",
"serde", "serde",
@ -5064,9 +5075,9 @@ dependencies = [
[[package]] [[package]]
name = "webauthn-rs-core" name = "webauthn-rs-core"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6528b4769d8fbe020e0e8c2e66bab6035982a2e9c3f8dac384120718f4763f4" checksum = "8ef9a989b5cd2c39c52850c4bc36ebc88907a06ceacf7f80e45d78a308944b9d"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"base64urlsafedata", "base64urlsafedata",
@ -5088,9 +5099,9 @@ dependencies = [
[[package]] [[package]]
name = "webauthn-rs-proto" name = "webauthn-rs-proto"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09e9a265574f8d7b8f8c94c4488bb491a82d7b8e183003a4512d3dceb88efd98" checksum = "585c7662de492733e6ea11e2726270bf34f5d7f8dbd08cd089830fbb3639a229"
dependencies = [ dependencies = [
"base64urlsafedata", "base64urlsafedata",
"js-sys", "js-sys",

View file

@ -158,10 +158,10 @@ wasm-bindgen = "^0.2.81"
wasm-bindgen-futures = "^0.4.30" wasm-bindgen-futures = "^0.4.30"
wasm-bindgen-test = "0.3.33" wasm-bindgen-test = "0.3.33"
webauthn-authenticator-rs = "0.4.7" webauthn-authenticator-rs = "0.4.8"
webauthn-rs = "0.4.7" webauthn-rs = "0.4.8"
webauthn-rs-core = "0.4.7" webauthn-rs-core = "0.4.8"
webauthn-rs-proto = "0.4.7" webauthn-rs-proto = "0.4.8"
# webauthn-authenticator-rs = { path = "../webauthn-rs/webauthn-authenticator-rs" } # webauthn-authenticator-rs = { path = "../webauthn-rs/webauthn-authenticator-rs" }
# webauthn-rs = { path = "../webauthn-rs/webauthn-rs" } # webauthn-rs = { path = "../webauthn-rs/webauthn-rs" }
# webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" } # webauthn-rs-core = { path = "../webauthn-rs/webauthn-rs-core" }

View file

@ -18,6 +18,7 @@ wasm = ["webauthn-rs-proto/wasm"]
[dependencies] [dependencies]
base32.workspace = true base32.workspace = true
base64urlsafedata.workspace = true base64urlsafedata.workspace = true
num_enum.workspace = true
scim_proto.workspace = true scim_proto.workspace = true
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true serde_json.workspace = true

View file

@ -1,7 +1,9 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::fmt; use std::fmt;
use std::str::FromStr;
use num_enum::TryFromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use webauthn_rs_proto::{ 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")] #[serde(rename_all = "lowercase")]
#[derive(TryFromPrimitive)]
#[repr(u16)]
pub enum UiHint { 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)] #[derive(Debug, Serialize, Deserialize, Clone)]

View file

@ -139,7 +139,7 @@ fn from_vec_dbval1(attr_val: Vec<DbValueV1>) -> Result<DbValueSetV2, OperationEr
let vs: Result<Vec<_>, _> = viter let vs: Result<Vec<_>, _> = viter
.map(|dbv| { .map(|dbv| {
if let DbValueV1::IndexType(s) = dbv { if let DbValueV1::IndexType(s) = dbv {
Ok(s) u16::try_from(s).map_err(|_| OperationError::InvalidValueState)
} else { } else {
Err(OperationError::InvalidValueState) Err(OperationError::InvalidValueState)
} }

View file

@ -495,7 +495,7 @@ pub enum DbValueSetV2 {
#[serde(rename = "SY")] #[serde(rename = "SY")]
SyntaxType(Vec<u16>), SyntaxType(Vec<u16>),
#[serde(rename = "IN")] #[serde(rename = "IN")]
IndexType(Vec<usize>), IndexType(Vec<u16>),
#[serde(rename = "RF")] #[serde(rename = "RF")]
Reference(Vec<Uuid>), Reference(Vec<Uuid>),
#[serde(rename = "JF")] #[serde(rename = "JF")]
@ -550,11 +550,13 @@ pub enum DbValueSetV2 {
JwsKeyRs256(Vec<Vec<u8>>), JwsKeyRs256(Vec<Vec<u8>>),
#[serde(rename = "AS")] #[serde(rename = "AS")]
Oauth2Session(Vec<DbValueOauth2Session>), Oauth2Session(Vec<DbValueOauth2Session>),
#[serde(rename = "UH")]
UiHint(Vec<u16>),
} }
impl DbValueSetV2 { impl DbValueSetV2 {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match &self { match self {
DbValueSetV2::Utf8(set) => set.len(), DbValueSetV2::Utf8(set) => set.len(),
DbValueSetV2::Iutf8(set) => set.len(), DbValueSetV2::Iutf8(set) => set.len(),
DbValueSetV2::Iname(set) => set.len(), DbValueSetV2::Iname(set) => set.len(),
@ -589,6 +591,7 @@ impl DbValueSetV2 {
DbValueSetV2::Oauth2Session(set) => set.len(), DbValueSetV2::Oauth2Session(set) => set.len(),
DbValueSetV2::JwsKeyEs256(set) => set.len(), DbValueSetV2::JwsKeyEs256(set) => set.len(),
DbValueSetV2::JwsKeyRs256(set) => set.len(), DbValueSetV2::JwsKeyRs256(set) => set.len(),
DbValueSetV2::UiHint(set) => set.len(),
} }
} }

View file

@ -3,6 +3,7 @@ use crate::constants::uuids::*;
use crate::constants::values::*; use crate::constants::values::*;
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew}; use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
use crate::value::Value; use crate::value::Value;
use kanidm_proto::v1::UiHint;
#[cfg(test)] #[cfg(test)]
use uuid::{uuid, Uuid}; 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. /// 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#"{ pub const JSON_IDM_HIGH_PRIVILEGE_V1: &str = r#"{
"attrs": { "attrs": {

View file

@ -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 === // === classes ===
pub const JSON_SCHEMA_CLASS_PERSON: &str = r#" pub const JSON_SCHEMA_CLASS_PERSON: &str = r#"
@ -1337,7 +1368,8 @@ pub const JSON_SCHEMA_CLASS_GROUP: &str = r#"
"group" "group"
], ],
"systemmay": [ "systemmay": [
"member" "member",
"grant_ui_hint"
], ],
"systemmust": [ "systemmust": [
"name", "name",

View file

@ -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 = pub const _UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: Uuid =
uuid!("00000000-0000-0000-0000-000000000037"); 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"); 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_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_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_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 // System and domain infos
// I'd like to strongly criticise william of the past for making poor choices about these allocations. // I'd like to strongly criticise william of the past for making poor choices about these allocations.

View file

@ -39,6 +39,7 @@ lazy_static! {
pub static ref CLASS_ACCOUNT: Value = Value::new_class("account"); 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_DOMAIN_INFO: Value = Value::new_class("domain_info");
pub static ref CLASS_DYNGROUP: Value = Value::new_class("dyngroup"); 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_MEMBEROF: Value = Value::new_class("memberof");
pub static ref CLASS_OBJECT: Value = Value::new_class("object"); pub static ref CLASS_OBJECT: Value = Value::new_class("object");
pub static ref CLASS_RECYCLED: Value = Value::new_class("recycled"); pub static ref CLASS_RECYCLED: Value = Value::new_class("recycled");

View file

@ -33,6 +33,7 @@ use compact_jwt::JwsSigner;
use hashbrown::HashMap; use hashbrown::HashMap;
use kanidm_proto::v1::{ use kanidm_proto::v1::{
ConsistencyError, Entry as ProtoEntry, Filter as ProtoFilter, OperationError, SchemaError, ConsistencyError, Entry as ProtoEntry, Filter as ProtoFilter, OperationError, SchemaError,
UiHint,
}; };
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry}; use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
use smartstring::alias::String as AttrString; 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()) 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)] #[inline(always)]
/// Return a single secret value, if valid to transform this value. /// Return a single secret value, if valid to transform this value.
pub fn get_ava_single_secret(&self, attr: &str) -> Option<&str> { pub fn get_ava_single_secret(&self, attr: &str) -> Option<&str> {

View file

@ -91,7 +91,13 @@ macro_rules! try_from_entry {
.cloned() .cloned()
.unwrap_or_default(); .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) { if $value.attribute_equality("class", &PVCLASS_POSIXACCOUNT) {
ui_hints.insert(UiHint::PosixAccount); ui_hints.insert(UiHint::PosixAccount);
@ -670,9 +676,10 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::constants::JSON_ANONYMOUS_V1; use crate::event::{CreateEvent, ModifyEvent};
// use crate::entry::{Entry, EntryNew, EntrySealed}; use crate::prelude::*;
// use crate::idm::account::Account; use async_std::task;
use kanidm_proto::v1::{AuthType, UiHint};
#[test] #[test]
fn test_idm_account_from_anonymous() { fn test_idm_account_from_anonymous() {
@ -682,14 +689,94 @@ mod tests {
} }
#[test] #[test]
fn test_idm_account_from_real() { fn test_idm_account_ui_hints() {
// For now, nothing, but later, we'll test different types of cred run_idm_test!(|_qs: &QueryServer,
// passing. 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] let target_uuid = Uuid::new_v4();
fn test_idm_account_set_credential() {
// Using a real entry, set a credential back to it's entry. // Create a user. So far no ui hints.
// In the end, this boils down to a modify operation on the Value // 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());
})
} }
} }

View file

@ -1,3 +1,6 @@
use std::collections::BTreeSet;
use kanidm_proto::v1::UiHint;
use kanidm_proto::v1::{Group as ProtoGroup, OperationError}; use kanidm_proto::v1::{Group as ProtoGroup, OperationError};
use uuid::Uuid; use uuid::Uuid;
@ -10,6 +13,7 @@ pub struct Group {
spn: String, spn: String,
uuid: Uuid, uuid: Uuid,
// We'll probably add policy and claims later to this // We'll probably add policy and claims later to this
pub ui_hints: BTreeSet<UiHint>,
} }
macro_rules! try_from_account_e { 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( let spn = $value.get_ava_single_proto_string("spn").ok_or(
OperationError::InvalidAccountState("Missing attribute: spn".to_string()), OperationError::InvalidAccountState("Missing attribute: spn".to_string()),
)?; )?;
let uuid = $value.get_uuid(); 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") { let mut groups: Vec<Group> = match $value.get_ava_as_refuuid("memberof") {
Some(riter) => { Some(riter) => {
@ -111,7 +123,16 @@ impl Group {
let uuid = value.get_uuid(); 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 { pub fn to_proto(&self) -> ProtoGroup {

View file

@ -197,6 +197,7 @@ impl SchemaAttribute {
// These are just insensitive string lookups on the hex-ified kid. // These are just insensitive string lookups on the hex-ified kid.
SyntaxType::JwsKeyEs256 => matches!(v, PartialValue::Iutf8(_)), SyntaxType::JwsKeyEs256 => matches!(v, PartialValue::Iutf8(_)),
SyntaxType::JwsKeyRs256 => matches!(v, PartialValue::Iutf8(_)), SyntaxType::JwsKeyRs256 => matches!(v, PartialValue::Iutf8(_)),
SyntaxType::UiHint => matches!(v, PartialValue::UiHint(_)),
}; };
if r { if r {
Ok(()) Ok(())
@ -243,6 +244,7 @@ impl SchemaAttribute {
SyntaxType::Oauth2Session => matches!(v, Value::Oauth2Session(_, _)), SyntaxType::Oauth2Session => matches!(v, Value::Oauth2Session(_, _)),
SyntaxType::JwsKeyEs256 => matches!(v, Value::JwsKeyEs256(_)), SyntaxType::JwsKeyEs256 => matches!(v, Value::JwsKeyEs256(_)),
SyntaxType::JwsKeyRs256 => matches!(v, Value::JwsKeyRs256(_)), SyntaxType::JwsKeyRs256 => matches!(v, Value::JwsKeyRs256(_)),
SyntaxType::UiHint => matches!(v, Value::UiHint(_)),
}; };
if r { if r {
Ok(()) Ok(())

View file

@ -4,13 +4,14 @@
// This is really only used for long lived, high level types that need clone // This is really only used for long lived, high level types that need clone
// that otherwise can't be cloned. Think Mutex. // that otherwise can't be cloned. Think Mutex.
use std::cell::Cell; use std::cell::Cell;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn}; use concread::arcache::{ARCache, ARCacheBuilder, ARCacheReadTxn};
use concread::cowcell::*; use concread::cowcell::*;
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use kanidm_proto::v1::{ConsistencyError, SchemaError}; use kanidm_proto::v1::{ConsistencyError, SchemaError, UiHint};
use tokio::sync::{Semaphore, SemaphorePermit}; use tokio::sync::{Semaphore, SemaphorePermit};
use tracing::trace; 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::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::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::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 => { 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 => { None => {
@ -2682,6 +2691,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
JSON_SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME, JSON_SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME,
JSON_SCHEMA_ATTR_SYNC_TOKEN_SESSION, JSON_SCHEMA_ATTR_SYNC_TOKEN_SESSION,
JSON_SCHEMA_ATTR_SYNC_COOKIE, JSON_SCHEMA_ATTR_SYNC_COOKIE,
JSON_SCHEMA_ATTR_GRANT_UI_HINT,
JSON_SCHEMA_CLASS_PERSON, JSON_SCHEMA_CLASS_PERSON,
JSON_SCHEMA_CLASS_ORGPERSON, JSON_SCHEMA_CLASS_ORGPERSON,
JSON_SCHEMA_CLASS_GROUP, JSON_SCHEMA_CLASS_GROUP,
@ -2848,13 +2858,26 @@ impl<'a> QueryServerWriteTransaction<'a> {
debug_assert!(res.is_ok()); debug_assert!(res.is_ok());
res?; 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_schema.set(true);
self.changed_acp.set(true); self.changed_acp.set(true);
Ok(()) Ok(())
} }
#[instrument(level = "info", name = "reload_schema", skip(self))] #[instrument(level = "debug", name = "reload_schema", skip(self))]
fn reload_schema(&mut self) -> Result<(), OperationError> { fn reload_schema(&mut self) -> Result<(), OperationError> {
// supply entries to the writable schema to reload from. // supply entries to the writable schema to reload from.
// find all attributes. // find all attributes.

View file

@ -12,6 +12,8 @@ use std::time::Duration;
use compact_jwt::JwsSigner; use compact_jwt::JwsSigner;
use hashbrown::HashSet; use hashbrown::HashSet;
use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::Filter as ProtoFilter;
use kanidm_proto::v1::UiHint;
use num_enum::TryFromPrimitive;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sshkeys::PublicKey as SshPublicKey; use sshkeys::PublicKey as SshPublicKey;
@ -72,14 +74,6 @@ pub struct Address {
pub country: String, 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)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum IntentTokenState { pub enum IntentTokenState {
Valid { 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 { impl TryFrom<&str> for IndexType {
type Error = (); 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 { impl IndexType {
pub fn as_idx_str(&self) -> &str { pub fn as_idx_str(&self) -> &str {
match self { match self {
@ -132,14 +134,6 @@ impl IndexType {
IndexType::SubString => "sub", 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 { impl fmt::Display for IndexType {
@ -157,7 +151,19 @@ impl fmt::Display for IndexType {
} }
#[allow(non_camel_case_types)] #[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)] #[repr(u16)]
pub enum SyntaxType { pub enum SyntaxType {
Utf8String = 0, Utf8String = 0,
@ -189,6 +195,7 @@ pub enum SyntaxType {
JwsKeyEs256 = 26, JwsKeyEs256 = 26,
JwsKeyRs256 = 27, JwsKeyRs256 = 27,
Oauth2Session = 28, Oauth2Session = 28,
UiHint = 29,
} }
impl TryFrom<&str> for SyntaxType { impl TryFrom<&str> for SyntaxType {
@ -227,45 +234,7 @@ impl TryFrom<&str> for SyntaxType {
"JWS_KEY_ES256" => Ok(SyntaxType::JwsKeyEs256), "JWS_KEY_ES256" => Ok(SyntaxType::JwsKeyEs256),
"JWS_KEY_RS256" => Ok(SyntaxType::JwsKeyRs256), "JWS_KEY_RS256" => Ok(SyntaxType::JwsKeyRs256),
"OAUTH2SESSION" => Ok(SyntaxType::Oauth2Session), "OAUTH2SESSION" => Ok(SyntaxType::Oauth2Session),
_ => Err(()), "UIHINT" => Ok(SyntaxType::UiHint),
}
}
}
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),
_ => Err(()), _ => Err(()),
} }
} }
@ -303,6 +272,7 @@ impl fmt::Display for SyntaxType {
SyntaxType::JwsKeyEs256 => "JWS_KEY_ES256", SyntaxType::JwsKeyEs256 => "JWS_KEY_ES256",
SyntaxType::JwsKeyRs256 => "JWS_KEY_RS256", SyntaxType::JwsKeyRs256 => "JWS_KEY_RS256",
SyntaxType::Oauth2Session => "OAUTH2SESSION", SyntaxType::Oauth2Session => "OAUTH2SESSION",
SyntaxType::UiHint => "UIHINT",
}) })
} }
} }
@ -348,6 +318,7 @@ pub enum PartialValue {
// Float64(f64), // Float64(f64),
RestrictedString(String), RestrictedString(String),
IntentToken(String), IntentToken(String),
UiHint(UiHint),
Passkey(Uuid), Passkey(Uuid),
DeviceKey(Uuid), DeviceKey(Uuid),
@ -697,7 +668,7 @@ impl PartialValue {
} }
pub fn get_idx_eq_key(&self) -> String { pub fn get_idx_eq_key(&self) -> String {
match &self { match self {
PartialValue::Utf8(s) PartialValue::Utf8(s)
| PartialValue::Iutf8(s) | PartialValue::Iutf8(s)
| PartialValue::Iname(s) | PartialValue::Iname(s)
@ -736,6 +707,7 @@ impl PartialValue {
PartialValue::IntentToken(u) => u.clone(), PartialValue::IntentToken(u) => u.clone(),
PartialValue::TrustedDeviceEnrollment(u) => u.as_hyphenated().to_string(), PartialValue::TrustedDeviceEnrollment(u) => u.as_hyphenated().to_string(),
PartialValue::Session(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), JwsKeyEs256(JwsSigner),
JwsKeyRs256(JwsSigner), JwsKeyRs256(JwsSigner),
UiHint(UiHint),
} }
impl PartialEq for Value { impl PartialEq for Value {

View file

@ -20,9 +20,9 @@ impl ValueSetIndex {
self.set.insert(s) 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: 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 })) Ok(Box::new(ValueSetIndex { set }))
} }
@ -98,7 +98,7 @@ impl ValueSetT for ValueSetIndex {
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { 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> + '_> { fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {

View file

@ -4,6 +4,7 @@ use compact_jwt::JwsSigner;
use dyn_clone::DynClone; use dyn_clone::DynClone;
use hashbrown::HashSet; use hashbrown::HashSet;
use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::Filter as ProtoFilter;
use kanidm_proto::v1::UiHint;
use smolset::SmolSet; use smolset::SmolSet;
use time::OffsetDateTime; use time::OffsetDateTime;
// use std::fmt::Debug; // use std::fmt::Debug;
@ -36,6 +37,7 @@ mod session;
mod spn; mod spn;
mod ssh; mod ssh;
mod syntax; mod syntax;
mod uihint;
mod uint32; mod uint32;
mod url; mod url;
mod utf8; mod utf8;
@ -60,6 +62,7 @@ pub use self::session::{ValueSetOauth2Session, ValueSetSession};
pub use self::spn::ValueSetSpn; pub use self::spn::ValueSetSpn;
pub use self::ssh::ValueSetSshKey; pub use self::ssh::ValueSetSshKey;
pub use self::syntax::ValueSetSyntax; pub use self::syntax::ValueSetSyntax;
pub use self::uihint::ValueSetUiHint;
pub use self::uint32::ValueSetUint32; pub use self::uint32::ValueSetUint32;
pub use self::url::ValueSetUrl; pub use self::url::ValueSetUrl;
pub use self::utf8::ValueSetUtf8; pub use self::utf8::ValueSetUtf8;
@ -495,6 +498,16 @@ pub trait ValueSetT: std::fmt::Debug + DynClone {
debug_assert!(false); debug_assert!(false);
None 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 { impl PartialEq for ValueSet {
@ -546,6 +559,7 @@ pub fn from_result_value_iter(
Value::PublicBinary(t, b) => ValueSetPublicBinary::new(t, b), Value::PublicBinary(t, b) => ValueSetPublicBinary::new(t, b),
Value::IntentToken(u, s) => ValueSetIntentToken::new(u, s), Value::IntentToken(u, s) => ValueSetIntentToken::new(u, s),
Value::EmailAddress(a, _) => ValueSetEmailAddress::new(a), Value::EmailAddress(a, _) => ValueSetEmailAddress::new(a),
Value::UiHint(u) => ValueSetUiHint::new(u),
Value::PhoneNumber(_, _) Value::PhoneNumber(_, _)
| Value::Passkey(_, _, _) | Value::Passkey(_, _, _)
| Value::DeviceKey(_, _, _) | 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::JwsKeyRs256(k) => ValueSetJwsKeyRs256::new(k),
Value::Session(u, m) => ValueSetSession::new(u, m), Value::Session(u, m) => ValueSetSession::new(u, m),
Value::Oauth2Session(u, m) => ValueSetOauth2Session::new(u, m), Value::Oauth2Session(u, m) => ValueSetOauth2Session::new(u, m),
Value::UiHint(u) => ValueSetUiHint::new(u),
Value::PhoneNumber(_, _) | Value::TrustedDeviceEnrollment(_) => { Value::PhoneNumber(_, _) | Value::TrustedDeviceEnrollment(_) => {
debug_assert!(false); debug_assert!(false);
return Err(OperationError::InvalidValueState); 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::Oauth2Session(set) => ValueSetOauth2Session::from_dbvs2(set),
DbValueSetV2::JwsKeyEs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set), DbValueSetV2::JwsKeyEs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set),
DbValueSetV2::JwsKeyRs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set), DbValueSetV2::JwsKeyRs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set),
DbValueSetV2::UiHint(set) => ValueSetUiHint::from_dbvs2(set),
DbValueSetV2::PhoneNumber(_, _) | DbValueSetV2::TrustedDeviceEnrollment(_) => { DbValueSetV2::PhoneNumber(_, _) | DbValueSetV2::TrustedDeviceEnrollment(_) => {
todo!() todo!()
} }

View file

@ -22,7 +22,7 @@ impl ValueSetSyntax {
pub fn from_dbvs2(data: Vec<u16>) -> Result<ValueSet, OperationError> { pub fn from_dbvs2(data: Vec<u16>) -> Result<ValueSet, OperationError> {
let set: Result<_, _> = data.into_iter().map(SyntaxType::try_from).collect(); 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 })) Ok(Box::new(ValueSetSyntax { set }))
} }
} }

View 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()))
}
}

View file

@ -233,7 +233,7 @@ function addBorrowedObject(obj) {
} }
function __wbg_adapter_48(arg0, arg1, arg2) { function __wbg_adapter_48(arg0, arg1, arg2) {
try { 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 { } finally {
heap[stack_pointer++] = undefined; heap[stack_pointer++] = undefined;
} }
@ -261,11 +261,11 @@ function makeClosure(arg0, arg1, dtor, f) {
return real; return real;
} }
function __wbg_adapter_51(arg0, arg1, arg2) { 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) { 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() { function getImports() {
const imports = {}; const imports = {};
imports.wbg = {}; 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) { imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
const obj = getObject(arg1); const obj = getObject(arg1);
const ret = typeof(obj) === 'number' ? obj : undefined; const ret = typeof(obj) === 'number' ? obj : undefined;
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(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) { imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
const obj = getObject(arg1); const obj = getObject(arg1);
const ret = typeof(obj) === 'string' ? obj : undefined; const ret = typeof(obj) === 'string' ? obj : undefined;
@ -372,23 +359,14 @@ function getImports() {
getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0; 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) { imports.wbg.__wbindgen_bigint_from_i64 = function(arg0) {
const ret = arg0; const ret = arg0;
return addHeapObject(ret); 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) { imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0); const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret); return addHeapObject(ret);
@ -405,13 +383,35 @@ function getImports() {
const ret = getStringFromWasm0(arg0, arg1); const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret); 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) { imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(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) { imports.wbg.__wbindgen_cb_drop = function(arg0) {
const obj = takeObject(arg0).original; const obj = takeObject(arg0).original;
if (obj.cnt-- == 1) { if (obj.cnt-- == 1) {
@ -421,7 +421,7 @@ function getImports() {
const ret = false; const ret = false;
return ret; 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)); modal_hide_by_id(getStringFromWasm0(arg0, arg1));
}; };
imports.wbg.__wbindgen_number_new = function(arg0) { imports.wbg.__wbindgen_number_new = function(arg0) {
@ -539,6 +539,43 @@ function getImports() {
const ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2)); const ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) }; }, 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) { imports.wbg.__wbg_getItem_845e475f85f593e4 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3)); const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 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) { imports.wbg.__wbg_setItem_9c469d634d0c321c = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) }; }, 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) { imports.wbg.__wbg_pathname_78a642e573bf8169 = function(arg0, arg1) {
const ret = getObject(arg1).pathname; const ret = getObject(arg1).pathname;
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -573,69 +620,6 @@ function getImports() {
const ret = new URL(getStringFromWasm0(arg0, arg1)); const ret = new URL(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, 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) { imports.wbg.__wbg_instanceof_HtmlInputElement_970e4026de0fccff = function(arg0) {
let result; let result;
try { try {
@ -659,6 +643,40 @@ function getImports() {
imports.wbg.__wbg_setvalue_e5b519cca37d82a7 = function(arg0, arg1, arg2) { imports.wbg.__wbg_setvalue_e5b519cca37d82a7 = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(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) { imports.wbg.__wbg_instanceof_Element_33bd126d58f2021b = function(arg0) {
let result; let result;
try { try {
@ -702,21 +720,6 @@ function getImports() {
imports.wbg.__wbg_focus_adfe4cc61e2c09bc = function() { return handleError(function (arg0) { imports.wbg.__wbg_focus_adfe4cc61e2c09bc = function() { return handleError(function (arg0) {
getObject(arg0).focus(); getObject(arg0).focus();
}, arguments) }; }, 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) { imports.wbg.__wbg_credentials_eab5c0bffc3e9cc5 = function(arg0) {
const ret = getObject(arg0).credentials; const ret = getObject(arg0).credentials;
return addHeapObject(ret); return addHeapObject(ret);
@ -746,12 +749,6 @@ function getImports() {
imports.wbg.__wbg_preventDefault_3209279b490de583 = function(arg0) { imports.wbg.__wbg_preventDefault_3209279b490de583 = function(arg0) {
getObject(arg0).preventDefault(); 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) { imports.wbg.__wbg_newwithform_6b545e9ddaccc455 = function() { return handleError(function (arg0) {
const ret = new FormData(getObject(arg0)); const ret = new FormData(getObject(arg0));
return addHeapObject(ret); return addHeapObject(ret);
@ -760,6 +757,12 @@ function getImports() {
const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2)); const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2));
return addHeapObject(ret); 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) { imports.wbg.__wbg_parentElement_0cffb3ceb0f107bd = function(arg0) {
const ret = getObject(arg0).parentElement; const ret = getObject(arg0).parentElement;
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
@ -1037,16 +1040,16 @@ function getImports() {
const ret = wasm.memory; const ret = wasm.memory;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper5651 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper4757 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1343, __wbg_adapter_48); const ret = makeMutClosure(arg0, arg1, 1013, __wbg_adapter_48);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper5814 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper4941 = function(arg0, arg1, arg2) {
const ret = makeClosure(arg0, arg1, 1378, __wbg_adapter_51); const ret = makeClosure(arg0, arg1, 1037, __wbg_adapter_51);
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper6542 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper5597 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1635, __wbg_adapter_54); const ret = makeMutClosure(arg0, arg1, 1287, __wbg_adapter_54);
return addHeapObject(ret); return addHeapObject(ret);
}; };

View file

@ -5,7 +5,7 @@
"James Hodgkinson <james@terminaloutcomes.com>" "James Hodgkinson <james@terminaloutcomes.com>"
], ],
"description": "Kanidm Server Web User Interface", "description": "Kanidm Server Web User Interface",
"version": "1.1.0-alpha.10", "version": "1.1.0-alpha.11-dev",
"license": "MPL-2.0", "license": "MPL-2.0",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -1,5 +1,5 @@
use gloo::console; use gloo::console;
use kanidm_proto::v1::UserAuthToken; use kanidm_proto::v1::{UserAuthToken, UiHint};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsCast, UnwrapThrowExt}; use wasm_bindgen::{JsCast, UnwrapThrowExt};
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
@ -236,6 +236,8 @@ impl ViewsApp {
fn view_authenticated(&self, ctx: &Context<Self>, uat: &UserAuthToken) -> Html { fn view_authenticated(&self, ctx: &Context<Self>, uat: &UserAuthToken) -> Html {
let current_user_uat = uat.clone(); let current_user_uat = uat.clone();
let ui_hint_experimental = uat.ui_hints.contains(&UiHint::ExperimentalFeatures);
// WARN set dash-body against body here? // WARN set dash-body against body here?
html! { 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"> <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" /> <img src="/pkg/img/favicon.png" />
</button> </button>
<div class="collapse navbar-collapse" id="navbarCollapse"> <div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav me-auto mb-2 mb-md-0"> <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"> <li class="mb-1">
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Profile}> <Link<ViewRoute> classes="nav-link" to={ViewRoute::Apps}>
<span data-feather="file"></span> <span data-feather="file"></span>
{ "Profile" } { "Apps" }
</Link<ViewRoute>> </Link<ViewRoute>>
</li> </li>
<li class="mb-1"> if ui_hint_experimental {
<Link<ViewRoute> classes="nav-link" to={ViewRoute::Security}> <li class="mb-1">
<span data-feather="file"></span> <Link<ViewRoute> classes="nav-link" to={ViewRoute::Profile}>
{ "Security" } <span data-feather="file"></span>
</Link<ViewRoute>> { "Profile" }
</li> </Link<ViewRoute>>
// TODO: the admin link should only show up if you're an admin </li>
<li class="mb-1"> }
<Link<AdminRoute> classes="nav-link" to={AdminRoute::AdminMenu}>
<span data-feather="file"></span> <li class="mb-1">
{ "Admin" } <Link<ViewRoute> classes="nav-link" to={ViewRoute::Security}>
</Link<AdminRoute>> <span data-feather="file"></span>
</li> { "Security" }
<li class="mb-1"> </Link<ViewRoute>>
<a class="nav-link" href="#" </li>
data-bs-toggle="modal"
data-bs-target={format!("#{}", crate::constants::ID_SIGNOUTMODAL)} if ui_hint_experimental {
>{"Sign out"}</a> <li class="mb-1">
</li> <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> </ul>
<form class="d-flex"> <form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" /> <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" />