383 170 164 authentication updates 2 (#716)

Add foundations for credential update sessions.
This commit is contained in:
Firstyear 2022-04-27 10:56:18 +10:00 committed by GitHub
parent 61d7000870
commit 9ade567a52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 3101 additions and 529 deletions

39
Cargo.lock generated
View file

@ -601,21 +601,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "compact_jwt"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41e359e67b8bb65fdcdc67e506f65b4aec4a97c7e731f321ed1b4b86c443ca6e"
dependencies = [
"base64 0.13.0",
"openssl",
"serde",
"serde_json",
"tracing",
"url",
"uuid",
]
[[package]] [[package]]
name = "compact_jwt" name = "compact_jwt"
version = "0.2.0" version = "0.2.0"
@ -1891,7 +1876,7 @@ dependencies = [
"async-trait", "async-trait",
"base64 0.13.0", "base64 0.13.0",
"chrono", "chrono",
"compact_jwt 0.2.0", "compact_jwt",
"compiled-uuid", "compiled-uuid",
"concread", "concread",
"criterion", "criterion",
@ -1945,24 +1930,15 @@ dependencies = [
name = "kanidm_client" name = "kanidm_client"
version = "1.1.0-alpha.7" version = "1.1.0-alpha.7"
dependencies = [ dependencies = [
"async-std",
"base64 0.13.0",
"compact_jwt 0.1.9",
"futures",
"kanidm",
"kanidm_proto", "kanidm_proto",
"oauth2",
"reqwest", "reqwest",
"score",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
"toml", "toml",
"tracing", "tracing",
"tracing-subscriber",
"url", "url",
"uuid", "uuid",
"webauthn-authenticator-rs",
"webauthn-rs", "webauthn-rs",
] ]
@ -1983,7 +1959,7 @@ dependencies = [
name = "kanidm_tools" name = "kanidm_tools"
version = "1.1.0-alpha.7" version = "1.1.0-alpha.7"
dependencies = [ dependencies = [
"compact_jwt 0.2.0", "compact_jwt",
"dialoguer", "dialoguer",
"kanidm_client", "kanidm_client",
"kanidm_proto", "kanidm_proto",
@ -3193,22 +3169,31 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
"compact_jwt 0.2.0", "base64 0.13.0",
"compact_jwt",
"futures",
"futures-util", "futures-util",
"kanidm", "kanidm",
"kanidm_client",
"kanidm_proto", "kanidm_proto",
"ldap3_proto", "ldap3_proto",
"libc", "libc",
"oauth2",
"openssl", "openssl",
"profiles", "profiles",
"reqwest",
"serde", "serde",
"serde_json",
"tide", "tide",
"tide-openssl", "tide-openssl",
"tokio", "tokio",
"tokio-openssl", "tokio-openssl",
"tokio-util", "tokio-util",
"tracing", "tracing",
"tracing-subscriber",
"url",
"uuid", "uuid",
"webauthn-authenticator-rs",
] ]
[[package]] [[package]]

View file

@ -6,7 +6,7 @@ lto = "thin"
[workspace] [workspace]
members = [ members = [
"kanidm_proto", "kanidm_proto",
"kanidmd", "kanidmd/idm",
"kanidmd/score", "kanidmd/score",
"kanidmd/daemon", "kanidmd/daemon",
"kanidmd_web_ui", "kanidmd_web_ui",

View file

@ -4,7 +4,7 @@ IMAGE_BASE ?= kanidm
IMAGE_VERSION ?= devel IMAGE_VERSION ?= devel
EXT_OPTS ?= EXT_OPTS ?=
IMAGE_ARCH ?= "linux/amd64,linux/arm64" IMAGE_ARCH ?= "linux/amd64,linux/arm64"
ARGS ?= --build-arg "SCCACHE_REDIS=redis://172.24.20.4:6379" ARGS ?= --build-arg "SCCACHE_REDIS=redis://redis.dev.blackhats.net.au:6379"
BOOK_VERSION ?= master BOOK_VERSION ?= master

View file

@ -22,15 +22,3 @@ url = { version = "2", features = ["serde"] }
webauthn-rs = "0.3" webauthn-rs = "0.3"
tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] } tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
[dev-dependencies]
tracing-subscriber = "0.3"
tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
kanidm = { path = "../kanidmd" }
score = { path = "../kanidmd/score" }
futures = "0.3"
async-std = { version = "1.6", features = ["tokio1"] }
webauthn-authenticator-rs = "0.3.0-alpha.12"
oauth2_ext = { package = "oauth2", version = "4.0", default-features = false }
base64 = "0.13"
compact_jwt = "^0.1.5"

View file

@ -58,6 +58,43 @@ pub enum ConsistencyError {
BackendIndexSync, BackendIndexSync,
} }
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum PasswordFeedback {
// https://docs.rs/zxcvbn/latest/zxcvbn/feedback/enum.Suggestion.html
UseAFewWordsAvoidCommonPhrases,
NoNeedForSymbolsDigitsOrUppercaseLetters,
AddAnotherWordOrTwo,
CapitalizationDoesntHelpVeryMuch,
AllUppercaseIsAlmostAsEasyToGuessAsAllLowercase,
ReversedWordsArentMuchHarderToGuess,
PredictableSubstitutionsDontHelpVeryMuch,
UseALongerKeyboardPatternWithMoreTurns,
AvoidRepeatedWordsAndCharacters,
AvoidSequences,
AvoidRecentYears,
AvoidYearsThatAreAssociatedWithYou,
AvoidDatesAndYearsThatAreAssociatedWithYou,
// https://docs.rs/zxcvbn/latest/zxcvbn/feedback/enum.Warning.html
StraightRowsOfKeysAreEasyToGuess,
ShortKeyboardPatternsAreEasyToGuess,
RepeatsLikeAaaAreEasyToGuess,
RepeatsLikeAbcAbcAreOnlySlightlyHarderToGuess,
ThisIsATop10Password,
ThisIsATop100Password,
ThisIsACommonPassword,
ThisIsSimilarToACommonlyUsedPassword,
SequencesLikeAbcAreEasyToGuess,
RecentYearsAreEasyToGuess,
AWordByItselfIsEasyToGuess,
DatesAreOftenEasyToGuess,
NamesAndSurnamesByThemselvesAreEasyToGuess,
CommonNamesAndSurnamesAreEasyToGuess,
// Custom
TooShort(usize),
BadListed,
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum OperationError { pub enum OperationError {
@ -94,18 +131,17 @@ pub enum OperationError {
SerdeCborError, SerdeCborError,
AccessDenied, AccessDenied,
NotAuthenticated, NotAuthenticated,
NotAuthorised,
InvalidAuthState(String), InvalidAuthState(String),
InvalidSessionState, InvalidSessionState,
SystemProtectedObject, SystemProtectedObject,
SystemProtectedAttribute, SystemProtectedAttribute,
PasswordTooWeak, PasswordQuality(Vec<PasswordFeedback>),
PasswordTooShort(usize),
PasswordEmpty,
PasswordBadListed,
CryptographyError, CryptographyError,
ResourceLimit, ResourceLimit,
QueueDisconnected, QueueDisconnected,
Webauthn, Webauthn,
Wait(time::OffsetDateTime),
} }
impl PartialEq for OperationError { impl PartialEq for OperationError {

View file

@ -12,7 +12,7 @@ use webauthn_authenticator_rs::{u2fhid::U2FHid, WebauthnAuthenticator};
use kanidm_client::ClientError; use kanidm_client::ClientError;
use kanidm_client::ClientError::Http as ClientErrorHttp; use kanidm_client::ClientError::Http as ClientErrorHttp;
use kanidm_proto::v1::OperationError::{PasswordBadListed, PasswordTooShort, PasswordTooWeak}; use kanidm_proto::v1::OperationError::PasswordQuality;
impl AccountOpt { impl AccountOpt {
pub fn debug(&self) -> bool { pub fn debug(&self) -> bool {
@ -81,10 +81,10 @@ impl AccountOpt {
) { ) {
match e { match e {
// TODO: once the password length is configurable at a system level (#498), pull from the configuration. // TODO: once the password length is configurable at a system level (#498), pull from the configuration.
ClientErrorHttp(_, Some(PasswordBadListed), _) => error!("Password is banned by the administrator of this system, please try a different one."), ClientErrorHttp(_, Some(PasswordQuality(feedback)), _) => {
ClientErrorHttp(_, Some(PasswordTooShort(pwminlength)), _) => error!("Password was too short (needs to be at least {} characters), please try again.", pwminlength), error!("{:?}", feedback)
ClientErrorHttp(_, Some(PasswordTooWeak), _) => error!("The supplied password did not match configured complexity requirements, please use a password with upper/lower case letters, symbols and digits."), }
_ => error!("Error setting password -> {:?}", e) _ => error!("Error setting password -> {:?}", e),
} }
} }
} }

View file

@ -45,7 +45,7 @@ path = "src/test_auth.rs"
[dependencies] [dependencies]
kanidm_client = { path = "../kanidm_client", version = "1.1.0-alpha" } kanidm_client = { path = "../kanidm_client", version = "1.1.0-alpha" }
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha" } kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha" }
kanidm = { path = "../kanidmd", version = "1.1.0-alpha" } kanidm = { path = "../kanidmd/idm", version = "1.1.0-alpha" }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
@ -80,7 +80,7 @@ lru = "0.7"
# default = [ "libsqlite3-sys/bundled" ] # default = [ "libsqlite3-sys/bundled" ]
[dev-dependencies] [dev-dependencies]
kanidm = { path = "../kanidmd" } # kanidm = { path = "../kanidmd/idm" }
score = { path = "../kanidmd/score" } score = { path = "../kanidmd/score" }
[build-dependencies] [build-dependencies]

View file

@ -17,7 +17,7 @@ name = "kanidmd"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
kanidm = { path = "../" } kanidm = { path = "../idm" }
score = { path = "../score" } score = { path = "../score" }
structopt = { version = "0.3", default-features = false } structopt = { version = "0.3", default-features = false }
users = "0.11" users = "0.11"

View file

@ -8,7 +8,7 @@ use structopt::StructOpt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
include!("../src/lib/audit_loglevel.rs"); include!("../idm/src/audit_loglevel.rs");
include!("src/opt.rs"); include!("src/opt.rs");
fn main() { fn main() {

View file

@ -12,10 +12,10 @@ repository = "https://github.com/kanidm/kanidm/"
[lib] [lib]
name = "kanidm" name = "kanidm"
path = "src/lib/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha" } kanidm_proto = { path = "../../kanidm_proto", version = "1.1.0-alpha" }
tracing = { version = "0.1", features = ["attributes"] } tracing = { version = "0.1", features = ["attributes"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-serde = "0.1" tracing-serde = "0.1"
@ -97,7 +97,7 @@ criterion = { version = "0.3", features = ["html_reports"] }
webauthn-authenticator-rs = "0.3.0-alpha.12" webauthn-authenticator-rs = "0.3.0-alpha.12"
[build-dependencies] [build-dependencies]
profiles = { path = "../profiles" } profiles = { path = "../../profiles" }
[[bench]] [[bench]]
name = "scaling_10k" name = "scaling_10k"

View file

@ -3,7 +3,6 @@ use criterion::{
}; };
use kanidm; use kanidm;
use kanidm::audit::AuditScope;
use kanidm::entry::{Entry, EntryInit, EntryNew}; use kanidm::entry::{Entry, EntryInit, EntryNew};
use kanidm::entry_init; use kanidm::entry_init;
use kanidm::idm::server::{IdmServer, IdmServerDelayed}; use kanidm::idm::server::{IdmServer, IdmServerDelayed};
@ -30,10 +29,7 @@ pub fn scaling_user_create_single(c: &mut Criterion) {
for _i in 0..iters { for _i in 0..iters {
kanidm::macros::run_idm_test_no_logging( kanidm::macros::run_idm_test_no_logging(
|_qs: &QueryServer, |_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed| {
idms: &IdmServer,
_idms_delayed: &IdmServerDelayed,
au: &mut AuditScope| {
let ct = duration_from_epoch_now(); let ct = duration_from_epoch_now();
let start = Instant::now(); let start = Instant::now();
@ -50,10 +46,10 @@ pub fn scaling_user_create_single(c: &mut Criterion) {
("displayname", Value::new_utf8s(&name)) ("displayname", Value::new_utf8s(&name))
); );
let cr = idms_prox_write.qs_write.internal_create(au, vec![e1]); let cr = idms_prox_write.qs_write.internal_create(vec![e1]);
assert!(cr.is_ok()); assert!(cr.is_ok());
idms_prox_write.commit(au).expect("Must not fail"); idms_prox_write.commit().expect("Must not fail");
} }
elapsed = elapsed.checked_add(start.elapsed()).unwrap(); elapsed = elapsed.checked_add(start.elapsed()).unwrap();
}, },
@ -97,19 +93,16 @@ pub fn scaling_user_create_batched(c: &mut Criterion) {
for _i in 0..iters { for _i in 0..iters {
kanidm::macros::run_idm_test_no_logging( kanidm::macros::run_idm_test_no_logging(
|_qs: &QueryServer, |_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed| {
idms: &IdmServer,
_idms_delayed: &IdmServerDelayed,
au: &mut AuditScope| {
let ct = duration_from_epoch_now(); let ct = duration_from_epoch_now();
let start = Instant::now(); let start = Instant::now();
let idms_prox_write = task::block_on(idms.proxy_write_async(ct)); let idms_prox_write = task::block_on(idms.proxy_write_async(ct));
let cr = idms_prox_write.qs_write.internal_create(au, data.clone()); let cr = idms_prox_write.qs_write.internal_create(data.clone());
assert!(cr.is_ok()); assert!(cr.is_ok());
idms_prox_write.commit(au).expect("Must not fail"); idms_prox_write.commit().expect("Must not fail");
elapsed = elapsed.checked_add(start.elapsed()).unwrap(); elapsed = elapsed.checked_add(start.elapsed()).unwrap();
}, },
); );

View file

@ -367,6 +367,17 @@ impl AccessControlProfile {
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub struct AccessEffectivePermission {
// I don't think we need this? The ident is implied by the requestor.
// ident: Uuid,
pub target: Uuid,
pub search: BTreeSet<AttrString>,
pub modify_pres: BTreeSet<AttrString>,
pub modify_rem: BTreeSet<AttrString>,
pub modify_class: BTreeSet<AttrString>,
}
// ========================================================================= // =========================================================================
// ACP transactions and management for server bits. // ACP transactions and management for server bits.
// ========================================================================= // =========================================================================
@ -400,8 +411,8 @@ pub trait AccessControlsTransaction<'a> {
fn search_related_acp<'b>( fn search_related_acp<'b>(
&'b self, &'b self,
rec_entry: &Entry<EntrySealed, EntryCommitted>, rec_entry: &Entry<EntrySealed, EntryCommitted>,
se: &SearchEvent, ident: &Identity,
) -> Vec<&'b AccessControlSearch> { ) -> Vec<(&'b AccessControlSearch, Filter<FilterValidResolved>)> {
let search_state = self.get_search(); let search_state = self.get_search();
// let acp_related_search_cache = self.get_acp_related_search_cache(); // let acp_related_search_cache = self.get_acp_related_search_cache();
let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
@ -432,12 +443,11 @@ pub trait AccessControlsTransaction<'a> {
} else { } else {
*/ */
// else, we calculate this, and then stash/cache the uuids. // else, we calculate this, and then stash/cache the uuids.
let related_acp: Vec<&AccessControlSearch> = let related_acp: Vec<(&AccessControlSearch, Filter<FilterValidResolved>)> =
spanned!("access::search_related_acp<uncached>", { spanned!("access::search_related_acp<uncached>", {
search_state search_state
.iter() .iter()
// .filter_map(|(_, acs)| { .filter_map(|acs| {
.filter(|acs| {
// Now resolve the receiver filter // Now resolve the receiver filter
// Okay, so in filter resolution, the primary error case // Okay, so in filter resolution, the primary error case
// is that we have a non-user in the event. We have already // is that we have a non-user in the event. We have already
@ -454,17 +464,35 @@ pub trait AccessControlsTransaction<'a> {
// such that it takes an entry, rather than an event, but that // such that it takes an entry, rather than an event, but that
// would create issues in search. // would create issues in search.
match (&acs.acp.receiver).resolve( match (&acs.acp.receiver).resolve(
&se.ident, ident,
None, None,
Some(acp_resolve_filter_cache), Some(acp_resolve_filter_cache),
) { ) {
Ok(f_res) => rec_entry.entry_match_no_index(&f_res), Ok(f_res) => {
if rec_entry.entry_match_no_index(&f_res) {
// Now, for each of the acp's that apply to our receiver, resolve their
// related target filters.
(&acs.acp.targetscope)
.resolve(ident, None, Some(acp_resolve_filter_cache))
.map_err(|e| {
admin_error!(
?e,
"A internal filter/event was passed for resolution!?!?"
);
e
})
.ok()
.map(|f_res| (acs, f_res))
} else {
None
}
}
Err(e) => { Err(e) => {
admin_error!( admin_error!(
?e, ?e,
"A internal filter/event was passed for resolution!?!?" "A internal filter/event was passed for resolution!?!?"
); );
false None
} }
} }
}) })
@ -501,25 +529,8 @@ pub trait AccessControlsTransaction<'a> {
trace!(event = %se.ident, "Access check for search (filter) event"); trace!(event = %se.ident, "Access check for search (filter) event");
// First get the set of acps that apply to this receiver // First get the set of acps that apply to this receiver
let related_acp = self.search_related_acp(rec_entry, se); let related_acp: Vec<(&AccessControlSearch, _)> =
let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); self.search_related_acp(rec_entry, &se.ident);
let related_acp: Vec<(&AccessControlSearch, _)> = related_acp
.into_iter()
.filter_map(|acs| {
(&acs.acp.targetscope)
.resolve(&se.ident, None, Some(acp_resolve_filter_cache))
.map_err(|e| {
admin_error!(
?e,
"A internal filter/event was passed for resolution!?!?"
);
e
})
.ok()
.map(|f_res| (acs, f_res))
})
.collect();
/* /*
related_acp.iter().for_each(|racp| { related_acp.iter().for_each(|racp| {
@ -619,38 +630,20 @@ pub trait AccessControlsTransaction<'a> {
*/ */
trace!("Access check for search (reduce) event: {}", se.ident); trace!("Access check for search (reduce) event: {}", se.ident);
let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
// Get the relevant acps for this receiver. // Get the relevant acps for this receiver.
let related_acp = self.search_related_acp(rec_entry, se); let related_acp: Vec<(&AccessControlSearch, _)> =
self.search_related_acp(rec_entry, &se.ident);
let related_acp: Vec<&AccessControlSearch> = if let Some(r_attrs) = se.attrs.as_ref() { let related_acp: Vec<(&AccessControlSearch, _)> =
if let Some(r_attrs) = se.attrs.as_ref() {
related_acp related_acp
.into_iter() .into_iter()
.filter(|acs| !acs.attrs.is_disjoint(r_attrs)) .filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs))
.collect() .collect()
} else { } else {
related_acp related_acp
}; };
// Compile all the target filters in one pass.
let related_acp: Vec<(&AccessControlSearch, _)> = related_acp
.into_iter()
.filter_map(|acs| {
(&acs.acp.targetscope)
.resolve(&se.ident, None, Some(acp_resolve_filter_cache))
.map_err(|e| {
admin_error!(
?e,
"A internal filter/event was passed for resolution!?!?"
);
e
})
.ok()
.map(|f_res| (acs, f_res))
})
.collect();
/* /*
related_acp.iter().for_each(|racp| { related_acp.iter().for_each(|racp| {
lsecurity_access!( "Related acs -> {:?}", racp.acp.name); lsecurity_access!( "Related acs -> {:?}", racp.acp.name);
@ -729,48 +722,28 @@ pub trait AccessControlsTransaction<'a> {
}) })
} }
#[allow(clippy::cognitive_complexity)] fn modify_related_acp<'b>(
fn modify_allow_operation( &'b self,
&self, rec_entry: &Entry<EntrySealed, EntryCommitted>,
me: &ModifyEvent, ident: &Identity,
entries: &[Arc<EntrySealedCommitted>], ) -> Vec<(&'b AccessControlModify, Filter<FilterValidResolved>)> {
) -> Result<bool, OperationError> {
let rec_entry: &Entry<EntrySealed, EntryCommitted> = match &me.ident.origin {
IdentType::Internal => {
trace!("Internal operation, bypassing access check");
// No need to check ACS
return Ok(true);
}
IdentType::User(u) => &u.entry,
};
spanned!("access::modify_allow_operation", {
trace!("Access check for modify event: {}", me.ident);
// Some useful references we'll use for the remainder of the operation // Some useful references we'll use for the remainder of the operation
let modify_state = self.get_modify(); let modify_state = self.get_modify();
let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache(); let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
// Pre-check if the no-no purge class is present
let disallow = me
.modlist
.iter()
.any(|m| matches!(m, Modify::Purged(a) if a == "class"));
if disallow {
security_access!("Disallowing purge class in modification");
return Ok(false);
}
// Find the acps that relate to the caller, and compile their related // Find the acps that relate to the caller, and compile their related
// target filters. // target filters.
let related_acp: Vec<(&AccessControlModify, _)> = modify_state let related_acp: Vec<(&AccessControlModify, _)> = spanned!(
"access::modify_related_acp<uncached>",
{
modify_state
.iter() .iter()
.filter_map(|acs| { .filter_map(|acs| {
match (&acs.acp.receiver).resolve(&me.ident, None, Some(acp_resolve_filter_cache)) { match (&acs.acp.receiver).resolve(ident, None, Some(acp_resolve_filter_cache)) {
Ok(f_res) => { Ok(f_res) => {
if rec_entry.entry_match_no_index(&f_res) { if rec_entry.entry_match_no_index(&f_res) {
(&acs.acp.targetscope) (&acs.acp.targetscope)
.resolve(&me.ident, None, Some(acp_resolve_filter_cache)) .resolve(ident, None, Some(acp_resolve_filter_cache))
.map_err(|e| { .map_err(|e| {
admin_error!( admin_error!(
"A internal filter/event was passed for resolution!?!? {:?}", "A internal filter/event was passed for resolution!?!? {:?}",
@ -793,7 +766,45 @@ pub trait AccessControlsTransaction<'a> {
} }
} }
}) })
.collect(); .collect()
}
);
related_acp
}
#[allow(clippy::cognitive_complexity)]
fn modify_allow_operation(
&self,
me: &ModifyEvent,
entries: &[Arc<EntrySealedCommitted>],
) -> Result<bool, OperationError> {
let rec_entry: &Entry<EntrySealed, EntryCommitted> = match &me.ident.origin {
IdentType::Internal => {
trace!("Internal operation, bypassing access check");
// No need to check ACS
return Ok(true);
}
IdentType::User(u) => &u.entry,
};
spanned!("access::modify_allow_operation", {
trace!("Access check for modify event: {}", me.ident);
// Pre-check if the no-no purge class is present
let disallow = me
.modlist
.iter()
.any(|m| matches!(m, Modify::Purged(a) if a == "class"));
if disallow {
security_access!("Disallowing purge class in modification");
return Ok(false);
}
// Find the acps that relate to the caller, and compile their related
// target filters.
let related_acp: Vec<(&AccessControlModify, _)> =
self.modify_related_acp(&rec_entry, &me.ident);
related_acp.iter().for_each(|racp| { related_acp.iter().for_each(|racp| {
trace!("Related acs -> {:?}", racp.0.acp.name); trace!("Related acs -> {:?}", racp.0.acp.name);
@ -1137,6 +1148,143 @@ pub trait AccessControlsTransaction<'a> {
Ok(r) Ok(r)
}) })
} }
fn effective_permission_check(
&self,
ident: &Identity,
attrs: Option<BTreeSet<AttrString>>,
entries: &[Arc<EntrySealedCommitted>],
) -> Result<Vec<AccessEffectivePermission>, OperationError> {
// I think we need a structure like " CheckResult, which is in the order of the
// entries, but also stashes the uuid. Then it has search, mod, create, delete,
// as seperate attrs to describe what is capable.
// Does create make sense here? I don't think it does. Create requires you to
// have an entry template. I think james was right about the create being
// a template copy op ...
let rec_entry: &Entry<EntrySealed, EntryCommitted> = match &ident.origin {
IdentType::Internal => {
// In production we can't risk leaking data here, so we return
// empty sets.
security_critical!("IMPOSSIBLE STATE: Internal search in external interface?! Returning empty for safety.");
// No need to check ACS
return Err(OperationError::InvalidState);
}
IdentType::User(u) => &u.entry,
};
spanned!("access::effective_permission_check", {
trace!(ident = %ident, "Effective permission check");
// I think we seperate this to multiple checks ...?
// == search ==
// Get the relevant acps for this receiver.
let search_related_acp: Vec<(&AccessControlSearch, _)> =
self.search_related_acp(rec_entry, ident);
let search_related_acp: Vec<(&AccessControlSearch, _)> =
if let Some(r_attrs) = attrs.as_ref() {
search_related_acp
.into_iter()
.filter(|(acs, _)| !acs.attrs.is_disjoint(r_attrs))
.collect()
} else {
search_related_acp
};
/*
search_related_acp.iter().for_each(|(racp, _)| {
trace!("Related acs -> {:?}", racp.acp.name);
});
*/
// == modify ==
let modify_related_acp: Vec<(&AccessControlModify, _)> =
self.modify_related_acp(rec_entry, ident);
/*
modify_related_acp.iter().for_each(|(racp, _)| {
trace!("Related acm -> {:?}", racp.acp.name);
});
*/
let effective_permissions: Vec<_> = entries
.iter()
.map(|e| {
// == search ==
let allowed_attrs: BTreeSet<AttrString> = search_related_acp
.iter()
.filter_map(|(acs, f_res)| {
// if it applies
if e.entry_match_no_index(f_res) {
// security_access!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry matches acs");
Some(acs.attrs.iter().map(|s| s.clone()))
} else {
trace!(entry = ?e.get_uuid(), acs = %acs.acp.name, "entry DOES NOT match acs"); // should this be `security_access`?
None
}
})
.flatten()
.collect();
security_access!(
requested = ?attrs,
allows = ?allowed_attrs,
"attributes",
);
// intersect?
let search_effective = if let Some(r_attrs) = attrs.as_ref() {
r_attrs & &allowed_attrs
} else {
allowed_attrs
};
// == modify ==
let modify_scoped_acp: Vec<&AccessControlModify> = modify_related_acp
.iter()
.filter_map(|(acm, f_res)| {
if e.entry_match_no_index(f_res) {
Some(*acm)
} else {
None
}
})
.collect();
let modify_pres: BTreeSet<AttrString> = modify_scoped_acp
.iter()
.flat_map(|acp| acp.presattrs.iter().map(|v| v.clone()))
.collect();
let modify_rem: BTreeSet<AttrString> = modify_scoped_acp
.iter()
.flat_map(|acp| acp.remattrs.iter().map(|v| v.clone()))
.collect();
let modify_class: BTreeSet<AttrString> = modify_scoped_acp
.iter()
.flat_map(|acp| acp.classes.iter().map(|v| v.clone()))
.collect();
AccessEffectivePermission {
target: *e.get_uuid(),
search: search_effective,
modify_pres,
modify_rem,
modify_class,
}
})
.collect();
effective_permissions.iter().for_each(|ep| {
trace!(?ep);
});
Ok(effective_permissions)
})
}
} }
pub struct AccessControlsWriteTransaction<'a> { pub struct AccessControlsWriteTransaction<'a> {
@ -1359,10 +1507,12 @@ impl AccessControls {
mod tests { mod tests {
use crate::access::{ use crate::access::{
AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlProfile, AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlProfile,
AccessControlSearch, AccessControls, AccessControlsTransaction, AccessControlSearch, AccessControls, AccessControlsTransaction, AccessEffectivePermission,
}; };
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent};
use crate::prelude::*; use crate::prelude::*;
use compiled_uuid::uuid;
use std::collections::BTreeSet;
use std::sync::Arc; use std::sync::Arc;
macro_rules! acp_from_entry_err { macro_rules! acp_from_entry_err {
@ -2332,4 +2482,110 @@ mod tests {
// Test reject delete // Test reject delete
test_acp_delete!(&de_anon, vec![acp], &r_set, false); test_acp_delete!(&de_anon, vec![acp], &r_set, false);
} }
macro_rules! test_acp_effective_permissions {
(
$ident:expr,
$attrs:expr,
$search_controls:expr,
$modify_controls:expr,
$entries:expr,
$expect:expr
) => {{
let ac = AccessControls::new();
let mut acw = ac.write();
acw.update_search($search_controls)
.expect("Failed to update");
acw.update_modify($modify_controls)
.expect("Failed to update");
let acw = acw;
let res = acw
.effective_permission_check($ident, $attrs, $entries)
.expect("Failed to apply effective_permission_check");
debug!("result --> {:?}", res);
debug!("expect --> {:?}", $expect);
// should be ok, and same as expect.
assert!(res == $expect);
}};
}
#[test]
fn test_access_effective_permission_check_1() {
let _ = crate::tracing_tree::test_init();
let admin = unsafe { Identity::from_impersonate_entry_ser(JSON_ADMIN_V1) };
let e1: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(JSON_TESTPERSON1);
let ev1 = unsafe { e1.into_sealed_committed() };
let r_set = vec![Arc::new(ev1.clone())];
test_acp_effective_permissions!(
&admin,
None,
vec![unsafe {
AccessControlSearch::from_raw(
"test_acp",
"d38640c4-0254-49f9-99b7-8ba7d0233f3d",
// apply to admin only
filter_valid!(f_eq("name", PartialValue::new_iname("admin"))),
// Allow admin to read only testperson1
filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))),
// They can read "name".
"name",
)
}],
vec![],
&r_set,
vec![AccessEffectivePermission {
target: uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
search: btreeset![AttrString::from("name")],
modify_pres: BTreeSet::new(),
modify_rem: BTreeSet::new(),
modify_class: BTreeSet::new(),
}]
)
}
#[test]
fn test_access_effective_permission_check_2() {
let _ = crate::tracing_tree::test_init();
let admin = unsafe { Identity::from_impersonate_entry_ser(JSON_ADMIN_V1) };
let e1: Entry<EntryInit, EntryNew> = Entry::unsafe_from_entry_str(JSON_TESTPERSON1);
let ev1 = unsafe { e1.into_sealed_committed() };
let r_set = vec![Arc::new(ev1.clone())];
test_acp_effective_permissions!(
&admin,
None,
vec![],
vec![unsafe {
AccessControlModify::from_raw(
"test_acp",
"d38640c4-0254-49f9-99b7-8ba7d0233f3d",
// apply to admin only
filter_valid!(f_eq("name", PartialValue::new_iname("admin"))),
// Allow admin to read only testperson1
filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))),
// They can read "name".
"name",
"name",
"object",
)
}],
&r_set,
vec![AccessEffectivePermission {
target: uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
search: BTreeSet::new(),
modify_pres: btreeset![AttrString::from("name")],
modify_rem: btreeset![AttrString::from("name")],
modify_class: btreeset![AttrString::from("object")],
}]
)
}
} }

View file

@ -30,6 +30,16 @@ impl std::fmt::Debug for DbPasswordV1 {
} }
} }
#[derive(Serialize, Deserialize, Debug)]
pub enum DbValueIntentTokenStateV1 {
#[serde(rename = "v")]
Valid,
#[serde(rename = "p")]
InProgress(Uuid, Duration),
#[serde(rename = "c")]
Consumed,
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum DbTotpAlgoV1 { pub enum DbTotpAlgoV1 {
S1, S1,
@ -231,6 +241,15 @@ pub enum DbValueV1 {
PublicBinary(String, Vec<u8>), PublicBinary(String, Vec<u8>),
#[serde(rename = "RS")] #[serde(rename = "RS")]
RestrictedString(String), RestrictedString(String),
#[serde(rename = "IT")]
IntentToken {
u: Uuid,
s: DbValueIntentTokenStateV1,
},
#[serde(rename = "TE")]
TrustedDeviceEnrollment { u: Uuid },
#[serde(rename = "AS")]
AuthSession { u: Uuid },
} }
#[cfg(test)] #[cfg(test)]

View file

@ -112,7 +112,7 @@ pub const JSON_IDM_SELF_ACP_WRITE_V1: &str = r#"{
"uuid": ["00000000-0000-0000-0000-ffffff000021"], "uuid": ["00000000-0000-0000-0000-ffffff000021"],
"description": ["Builtin IDM Control for self write - required for people to update their own identities and credentials in line with best practices."], "description": ["Builtin IDM Control for self write - required for people to update their own identities and credentials in line with best practices."],
"acp_receiver": [ "acp_receiver": [
"{\"and\": [\"self\", {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}, {\"eq\": [\"uuid\", \"00000000-0000-0000-0000-ffffffffffff\"]}]}}]}" "{\"and\": [\"self\", {\"eq\": [\"class\", \"person\"]}, {\"eq\": [\"class\", \"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}, {\"eq\": [\"uuid\", \"00000000-0000-0000-0000-ffffffffffff\"]}]}}]}"
], ],
"acp_targetscope": [ "acp_targetscope": [
"{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"class\",\"account\"]}, \"self\"]}" "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"class\",\"account\"]}, \"self\"]}"

View file

@ -836,6 +836,35 @@ pub const JSON_SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE: &str = r#"{
} }
}"#; }"#;
pub const JSON_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN: &str = r#"{
"attrs": {
"class": [
"object",
"system",
"attributetype"
],
"description": [
"The status of a credential update intent token"
],
"index": [],
"unique": [
"false"
],
"multivalue": [
"true"
],
"attributename": [
"credential_update_intent_token"
],
"syntax": [
"INTENT_TOKEN"
],
"uuid": [
"00000000-0000-0000-0000-ffff00000096"
]
}
}"#;
// === classes === // === classes ===
pub const JSON_SCHEMA_CLASS_PERSON: &str = r#" pub const JSON_SCHEMA_CLASS_PERSON: &str = r#"
@ -939,6 +968,7 @@ pub const JSON_SCHEMA_CLASS_ACCOUNT: &str = r#"
], ],
"systemmay": [ "systemmay": [
"primary_credential", "primary_credential",
"credential_update_intent_token",
"ssh_publickey", "ssh_publickey",
"radius_secret", "radius_secret",
"account_expire", "account_expire",

View file

@ -165,6 +165,8 @@ pub const _UUID_SCHEMA_ATTR_RS256_PRIVATE_KEY_DER: Uuid =
pub const _UUID_SCHEMA_CLASS_ORGPERSON: Uuid = uuid!("00000000-0000-0000-0000-ffff00000094"); pub const _UUID_SCHEMA_CLASS_ORGPERSON: Uuid = uuid!("00000000-0000-0000-0000-ffff00000094");
pub const UUID_SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR: Uuid = pub const UUID_SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000095"); uuid!("00000000-0000-0000-0000-ffff00000095");
pub const _UUID_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000096");
// 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

@ -308,7 +308,7 @@ impl From<&Credential> for CredentialDetail {
CredentialDetailType::PasswordMfa( CredentialDetailType::PasswordMfa(
totp.is_some(), totp.is_some(),
labels, labels,
backup_code.iter().count(), backup_code.as_ref().map(|c| c.code_set.len()).unwrap_or(0),
) )
} }
}, },

View file

@ -32,7 +32,7 @@ use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction}; use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
use crate::value::{IndexType, SyntaxType}; use crate::value::{IndexType, SyntaxType};
use crate::value::{PartialValue, Value}; use crate::value::{IntentTokenState, PartialValue, Value};
use crate::valueset::ValueSet; use crate::valueset::ValueSet;
use kanidm_proto::v1::Entry as ProtoEntry; use kanidm_proto::v1::Entry as ProtoEntry;
use kanidm_proto::v1::Filter as ProtoFilter; use kanidm_proto::v1::Filter as ProtoFilter;
@ -1605,6 +1605,14 @@ impl<VALID, STATE> Entry<VALID, STATE> {
self.attrs.get(attr).and_then(|vs| vs.as_oauthscopemap()) self.attrs.get(attr).and_then(|vs| vs.as_oauthscopemap())
} }
#[inline(always)]
pub fn get_ava_as_intenttokens(
&self,
attr: &str,
) -> Option<&std::collections::BTreeMap<Uuid, IntentTokenState>> {
self.attrs.get(attr).and_then(|vs| vs.as_intenttoken())
}
#[inline(always)] #[inline(always)]
/// If possible, return an iterator over the set of values transformed into a `&str`. /// If possible, return an iterator over the set of values transformed into a `&str`.
pub fn get_ava_as_str(&self, attr: &str) -> Option<impl Iterator<Item = &str>> { pub fn get_ava_as_str(&self, attr: &str) -> Option<impl Iterator<Item = &str>> {
@ -1795,6 +1803,7 @@ impl<VALID, STATE> Entry<VALID, STATE> {
#[inline(always)] #[inline(always)]
/// Test if the following filter applies to and matches this entry. /// Test if the following filter applies to and matches this entry.
pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool { pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
let _entered = trace_span!("entry::entry_match_no_index").entered();
self.entry_match_no_index_inner(filter.to_inner()) self.entry_match_no_index_inner(filter.to_inner())
} }

View file

@ -748,6 +748,22 @@ impl AuthEventStep {
cred: AuthCredential::Password(pw.to_string()), cred: AuthCredential::Password(pw.to_string()),
}) })
} }
#[cfg(test)]
pub fn cred_step_totp(sid: Uuid, totp: u32) -> Self {
AuthEventStep::Cred(AuthEventStepCred {
sessionid: sid,
cred: AuthCredential::Totp(totp),
})
}
#[cfg(test)]
pub fn cred_step_backup_code(sid: Uuid, code: &str) -> Self {
AuthEventStep::Cred(AuthEventStepCred {
sessionid: sid,
cred: AuthCredential::BackupCode(code.to_string()),
})
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -804,6 +820,22 @@ impl AuthEvent {
step: AuthEventStep::cred_step_password(sid, pw), step: AuthEventStep::cred_step_password(sid, pw),
} }
} }
#[cfg(test)]
pub fn cred_step_totp(sid: Uuid, totp: u32) -> Self {
AuthEvent {
ident: None,
step: AuthEventStep::cred_step_totp(sid, totp),
}
}
#[cfg(test)]
pub fn cred_step_backup_code(sid: Uuid, code: &str) -> Self {
AuthEvent {
ident: None,
step: AuthEventStep::cred_step_backup_code(sid, code),
}
}
} }
// Probably should be a struct with the session id present. // Probably should be a struct with the session id present.

View file

@ -12,8 +12,9 @@ use crate::credential::totp::Totp;
use crate::credential::{softlock::CredSoftLockPolicy, Credential}; use crate::credential::{softlock::CredSoftLockPolicy, Credential};
use crate::idm::group::Group; use crate::idm::group::Group;
use crate::modify::{ModifyInvalid, ModifyList}; use crate::modify::{ModifyInvalid, ModifyList};
use crate::value::{PartialValue, Value}; use crate::value::{IntentTokenState, PartialValue, Value};
use std::collections::BTreeMap;
use std::time::Duration; use std::time::Duration;
use time::OffsetDateTime; use time::OffsetDateTime;
use uuid::Uuid; use uuid::Uuid;
@ -76,6 +77,11 @@ macro_rules! try_from_entry {
let uuid = $value.get_uuid().clone(); let uuid = $value.get_uuid().clone();
let credential_update_intent_tokens = $value
.get_ava_as_intenttokens("credential_update_intent_token")
.cloned()
.unwrap_or_else(|| BTreeMap::new());
Ok(Account { Ok(Account {
uuid, uuid,
name, name,
@ -88,6 +94,7 @@ macro_rules! try_from_entry {
spn, spn,
mail_primary, mail_primary,
mail, mail,
credential_update_intent_tokens,
}) })
}}; }};
} }
@ -110,14 +117,12 @@ pub(crate) struct Account {
pub valid_from: Option<OffsetDateTime>, pub valid_from: Option<OffsetDateTime>,
pub expire: Option<OffsetDateTime>, pub expire: Option<OffsetDateTime>,
pub radius_secret: Option<String>, pub radius_secret: Option<String>,
// primary: Credential
// app_creds: Vec<Credential>
// account expiry? (as opposed to cred expiry)
pub spn: String, pub spn: String,
// TODO #256: When you add mail, you should update the check to zxcvbn // TODO #256: When you add mail, you should update the check to zxcvbn
// to include these. // to include these.
pub mail_primary: Option<String>, pub mail_primary: Option<String>,
pub mail: Vec<String>, pub mail: Vec<String>,
pub credential_update_intent_tokens: BTreeMap<Uuid, IntentTokenState>,
} }
impl Account { impl Account {

View file

@ -585,6 +585,7 @@ impl AuthSession {
// of what's next, or ordering. // of what's next, or ordering.
let valid_mechs = auth_session.valid_auth_mechs(); let valid_mechs = auth_session.valid_auth_mechs();
security_info!(?valid_mechs, "Offering auth mechanisms");
let as_state = AuthState::Choose(valid_mechs); let as_state = AuthState::Choose(valid_mechs);
(Some(auth_session), as_state) (Some(auth_session), as_state)
} }
@ -629,6 +630,7 @@ impl AuthSession {
let allowed: Vec<_> = allowed_handler.next_auth_allowed(); let allowed: Vec<_> = allowed_handler.next_auth_allowed();
if allowed.is_empty() { if allowed.is_empty() {
security_info!("Unable to negotiate credentials");
( (
None, None,
Err(OperationError::InvalidAuthState( Err(OperationError::InvalidAuthState(
@ -642,6 +644,7 @@ impl AuthSession {
) )
} }
} else { } else {
security_error!("Unable to select a credential for authentication");
( (
Some(AuthSessionState::Denied(BAD_CREDENTIALS)), Some(AuthSessionState::Denied(BAD_CREDENTIALS)),
Ok(AuthState::Denied(BAD_CREDENTIALS.to_string())), Ok(AuthState::Denied(BAD_CREDENTIALS.to_string())),

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use uuid::Uuid;
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use webauthn_rs::proto::RegisterPublicKeyCredential; use webauthn_rs::proto::RegisterPublicKeyCredential;
@ -25,11 +23,11 @@ impl PasswordChangeEvent {
cleartext: String, cleartext: String,
// qs: &QueryServerWriteTransaction, // qs: &QueryServerWriteTransaction,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
let u = ident.get_uuid().ok_or(OperationError::InvalidState)?; let target = ident.get_uuid().ok_or(OperationError::InvalidState)?;
Ok(PasswordChangeEvent { Ok(PasswordChangeEvent {
ident, ident,
target: u, target,
cleartext, cleartext,
}) })
} }

View file

@ -5,6 +5,7 @@
pub(crate) mod account; pub(crate) mod account;
pub(crate) mod authsession; pub(crate) mod authsession;
pub(crate) mod credupdatesession;
pub(crate) mod delayed; pub(crate) mod delayed;
pub(crate) mod event; pub(crate) mod event;
pub(crate) mod group; pub(crate) mod group;

View file

@ -6,6 +6,7 @@ use crate::event::{AuthEvent, AuthEventStep, AuthResult};
use crate::identity::{IdentType, IdentUser, Limits}; use crate::identity::{IdentType, IdentUser, Limits};
use crate::idm::account::Account; use crate::idm::account::Account;
use crate::idm::authsession::AuthSession; use crate::idm::authsession::AuthSession;
use crate::idm::credupdatesession::CredentialUpdateSessionMutex;
use crate::idm::event::{ use crate::idm::event::{
AcceptSha1TotpEvent, CredentialStatusEvent, GeneratePasswordEvent, GenerateTotpEvent, AcceptSha1TotpEvent, CredentialStatusEvent, GeneratePasswordEvent, GenerateTotpEvent,
LdapAuthEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, LdapAuthEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
@ -38,8 +39,8 @@ use crate::idm::delayed::{
use hashbrown::HashSet; use hashbrown::HashSet;
use kanidm_proto::v1::{ use kanidm_proto::v1::{
AuthType, BackupCodesView, CredentialStatus, RadiusAuthToken, SetCredentialResponse, AuthType, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken,
UnixGroupToken, UnixUserToken, UserAuthToken, SetCredentialResponse, UnixGroupToken, UnixUserToken, UserAuthToken,
}; };
use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator}; use compact_jwt::{Jws, JwsSigner, JwsUnverified, JwsValidator};
@ -56,7 +57,7 @@ use core::task::{Context, Poll};
use futures::task as futures_task; use futures::task as futures_task;
use concread::{ use concread::{
bptree::{BptreeMap, BptreeMapWriteTxn}, bptree::{BptreeMap, BptreeMapReadTxn, BptreeMapWriteTxn},
cowcell::{CowCellReadTxn, CowCellWriteTxn}, cowcell::{CowCellReadTxn, CowCellWriteTxn},
hashmap::HashMap, hashmap::HashMap,
CowCell, CowCell,
@ -79,8 +80,6 @@ use tracing::trace;
type AuthSessionMutex = Arc<Mutex<AuthSession>>; type AuthSessionMutex = Arc<Mutex<AuthSession>>;
type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>; type CredSoftLockMutex = Arc<Mutex<CredSoftLock>>;
// type CredUpdateSessionMutex = Arc<Mutex<CredUpdateSession>>;
pub struct IdmServer { pub struct IdmServer {
// There is a good reason to keep this single thread - it // There is a good reason to keep this single thread - it
// means that limits to sessions can be easily applied and checked to // means that limits to sessions can be easily applied and checked to
@ -91,7 +90,7 @@ pub struct IdmServer {
softlocks: HashMap<Uuid, CredSoftLockMutex>, softlocks: HashMap<Uuid, CredSoftLockMutex>,
/// A set of in progress MFA registrations /// A set of in progress MFA registrations
mfareg_sessions: BptreeMap<Uuid, MfaRegSession>, mfareg_sessions: BptreeMap<Uuid, MfaRegSession>,
// cred_update_sessions: BptreeMap<Uuid, CredUpdateSessionMutex>, cred_update_sessions: BptreeMap<Uuid, CredentialUpdateSessionMutex>,
/// Reference to the query server. /// Reference to the query server.
qs: QueryServer, qs: QueryServer,
/// The configured crypto policy for the IDM server. Later this could be transactional and loaded from the db similar to access. But today it's just to allow dynamic pbkdf2rounds /// The configured crypto policy for the IDM server. Later this could be transactional and loaded from the db similar to access. But today it's just to allow dynamic pbkdf2rounds
@ -124,6 +123,16 @@ pub struct IdmServerAuthTransaction<'a> {
uat_jwt_validator: CowCellReadTxn<JwsValidator>, uat_jwt_validator: CowCellReadTxn<JwsValidator>,
} }
pub(crate) struct IdmServerCredUpdateTransaction<'a> {
pub _qs_read: QueryServerReadTransaction<'a>,
// sid: Sid,
pub _webauthn: &'a Webauthn<WebauthnDomainConfig>,
pub pw_badlist_cache: CowCellReadTxn<HashSet<String>>,
pub cred_update_sessions: BptreeMapReadTxn<'a, Uuid, CredentialUpdateSessionMutex>,
pub token_enc_key: CowCellReadTxn<Fernet>,
pub crypto_policy: &'a CryptoPolicy,
}
/// This contains read-only methods, like getting users, groups and other structured content. /// This contains read-only methods, like getting users, groups and other structured content.
pub struct IdmServerProxyReadTransaction<'a> { pub struct IdmServerProxyReadTransaction<'a> {
pub qs_read: QueryServerReadTransaction<'a>, pub qs_read: QueryServerReadTransaction<'a>,
@ -137,13 +146,14 @@ pub struct IdmServerProxyWriteTransaction<'a> {
pub qs_write: QueryServerWriteTransaction<'a>, pub qs_write: QueryServerWriteTransaction<'a>,
/// Associate to an event origin ID, which has a TS and a UUID instead /// Associate to an event origin ID, which has a TS and a UUID instead
mfareg_sessions: BptreeMapWriteTxn<'a, Uuid, MfaRegSession>, mfareg_sessions: BptreeMapWriteTxn<'a, Uuid, MfaRegSession>,
sid: Sid, pub(crate) cred_update_sessions: BptreeMapWriteTxn<'a, Uuid, CredentialUpdateSessionMutex>,
pub(crate) sid: Sid,
crypto_policy: &'a CryptoPolicy, crypto_policy: &'a CryptoPolicy,
webauthn: &'a Webauthn<WebauthnDomainConfig>, webauthn: &'a Webauthn<WebauthnDomainConfig>,
pw_badlist_cache: CowCellWriteTxn<'a, HashSet<String>>, pw_badlist_cache: CowCellWriteTxn<'a, HashSet<String>>,
uat_jwt_signer: CowCellWriteTxn<'a, JwsSigner>, uat_jwt_signer: CowCellWriteTxn<'a, JwsSigner>,
uat_jwt_validator: CowCellWriteTxn<'a, JwsValidator>, uat_jwt_validator: CowCellWriteTxn<'a, JwsValidator>,
token_enc_key: CowCellWriteTxn<'a, Fernet>, pub(crate) token_enc_key: CowCellWriteTxn<'a, Fernet>,
oauth2rs: Oauth2ResourceServersWriteTransaction<'a>, oauth2rs: Oauth2ResourceServersWriteTransaction<'a>,
} }
@ -246,6 +256,7 @@ impl IdmServer {
sessions: BptreeMap::new(), sessions: BptreeMap::new(),
softlocks: HashMap::new(), softlocks: HashMap::new(),
mfareg_sessions: BptreeMap::new(), mfareg_sessions: BptreeMap::new(),
cred_update_sessions: BptreeMap::new(),
qs, qs,
crypto_policy, crypto_policy,
async_tx, async_tx,
@ -312,6 +323,7 @@ impl IdmServer {
IdmServerProxyWriteTransaction { IdmServerProxyWriteTransaction {
mfareg_sessions: self.mfareg_sessions.write(), mfareg_sessions: self.mfareg_sessions.write(),
cred_update_sessions: self.cred_update_sessions.write(),
qs_write, qs_write,
sid, sid,
crypto_policy: &self.crypto_policy, crypto_policy: &self.crypto_policy,
@ -324,6 +336,23 @@ impl IdmServer {
} }
} }
#[cfg(test)]
pub(crate) fn cred_update_transaction(&self) -> IdmServerCredUpdateTransaction<'_> {
task::block_on(self.cred_update_transaction_async())
}
pub(crate) async fn cred_update_transaction_async(&self) -> IdmServerCredUpdateTransaction<'_> {
IdmServerCredUpdateTransaction {
_qs_read: self.qs.read_async().await,
// sid: Sid,
_webauthn: &self.webauthn,
pw_badlist_cache: self.pw_badlist_cache.read(),
cred_update_sessions: self.cred_update_sessions.read(),
token_enc_key: self.token_enc_key.read(),
crypto_policy: &self.crypto_policy,
}
}
#[cfg(test)] #[cfg(test)]
pub(crate) async fn delayed_action( pub(crate) async fn delayed_action(
&self, &self,
@ -574,6 +603,8 @@ impl<'a> IdmServerAuthTransaction<'a> {
// out of the session tree. // out of the session tree.
let account = Account::try_from_entry_ro(entry.as_ref(), &mut self.qs_read)?; let account = Account::try_from_entry_ro(entry.as_ref(), &mut self.qs_read)?;
trace!(?account.primary);
// Intent to take both trees to write. // Intent to take both trees to write.
let _session_ticket = self.session_ticket.acquire().await; let _session_ticket = self.session_ticket.acquire().await;
@ -1247,14 +1278,16 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
// is the password at least 10 char? // is the password at least 10 char?
if cleartext.len() < PW_MIN_LENGTH { if cleartext.len() < PW_MIN_LENGTH {
return Err(OperationError::PasswordTooShort(PW_MIN_LENGTH)); return Err(OperationError::PasswordQuality(vec![
PasswordFeedback::TooShort(PW_MIN_LENGTH),
]));
} }
// does the password pass zxcvbn? // does the password pass zxcvbn?
let entropy = zxcvbn::zxcvbn(cleartext, related_inputs).map_err(|e| { let entropy = zxcvbn::zxcvbn(cleartext, related_inputs).map_err(|e| {
admin_error!("zxcvbn check failure (password empty?) {:?}", e); admin_error!("zxcvbn check failure (password empty?) {:?}", e);
OperationError::PasswordEmpty OperationError::PasswordQuality(vec![PasswordFeedback::TooShort(PW_MIN_LENGTH)])
})?; })?;
// check account pwpolicy (for 3 or 4)? Do we need pw strength beyond this // check account pwpolicy (for 3 or 4)? Do we need pw strength beyond this
@ -1275,7 +1308,10 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
security_info!(?feedback, "pw quality feedback"); security_info!(?feedback, "pw quality feedback");
// return Err(OperationError::PasswordTooWeak(feedback)) // return Err(OperationError::PasswordTooWeak(feedback))
return Err(OperationError::PasswordTooWeak); // return Err(OperationError::PasswordTooWeak);
return Err(OperationError::PasswordQuality(vec![
PasswordFeedback::BadListed,
]));
} }
// check a password badlist to eliminate more content // check a password badlist to eliminate more content
@ -1283,7 +1319,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
// also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase) // also, when pw_badlist_cache is read from DB, it is read as Value (iutf8 lowercase)
if (&*self.pw_badlist_cache).contains(&cleartext.to_lowercase()) { if (&*self.pw_badlist_cache).contains(&cleartext.to_lowercase()) {
security_info!("Password found in badlist, rejecting"); security_info!("Password found in badlist, rejecting");
Err(OperationError::PasswordBadListed) Err(OperationError::PasswordQuality(vec![
PasswordFeedback::BadListed,
]))
} else { } else {
Ok(()) Ok(())
} }
@ -2073,6 +2111,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
self.token_enc_key.commit(); self.token_enc_key.commit();
self.pw_badlist_cache.commit(); self.pw_badlist_cache.commit();
self.mfareg_sessions.commit(); self.mfareg_sessions.commit();
self.cred_update_sessions.commit();
self.qs_write.commit() self.qs_write.commit()
}) })
} }
@ -3801,7 +3840,7 @@ mod tests {
} }
#[test] #[test]
fn test_idm_bundy_uat_expiry() { fn test_idm_jwt_uat_expiry() {
run_idm_test!( run_idm_test!(
|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed| { |qs: &QueryServer, idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed| {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
@ -3931,7 +3970,7 @@ mod tests {
} }
#[test] #[test]
fn test_idm_bundy_uat_token_key_reload() { fn test_idm_jwt_uat_token_key_reload() {
run_idm_test!( run_idm_test!(
|qs: &QueryServer, idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed| { |qs: &QueryServer, idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed| {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);

View file

@ -2,7 +2,7 @@
//! which is used to process authentication, store identities and enforce access controls. //! which is used to process authentication, store identities and enforce access controls.
#![recursion_limit = "512"] #![recursion_limit = "512"]
#![deny(warnings)] // #![deny(warnings)]
#![warn(unused_extern_crates)] #![warn(unused_extern_crates)]
#![deny(clippy::todo)] #![deny(clippy::todo)]
#![deny(clippy::unimplemented)] #![deny(clippy::unimplemented)]
@ -43,8 +43,8 @@ pub mod identity;
pub mod interval; pub mod interval;
pub mod ldap; pub mod ldap;
mod modify; mod modify;
pub(crate) mod value; pub mod value;
pub(crate) mod valueset; pub mod valueset;
#[macro_use] #[macro_use]
mod plugins; mod plugins;
mod access; mod access;

View file

@ -171,8 +171,6 @@ macro_rules! run_idm_test_inner {
use crate::prelude::*; use crate::prelude::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::schema::Schema; use crate::schema::Schema;
let _ = crate::tracing_tree::test_init();
/* /*
use env_logger; use env_logger;
::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug"); ::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug");
@ -213,6 +211,7 @@ where
&crate::idm::server::IdmServerDelayed, &crate::idm::server::IdmServerDelayed,
), ),
{ {
let _ = crate::tracing_tree::test_level(tracing::Level::ERROR);
let _ = run_idm_test_inner!(test_fn); let _ = run_idm_test_inner!(test_fn);
} }

View file

@ -196,6 +196,7 @@ impl SchemaAttribute {
SyntaxType::OauthScope => v.is_oauthscope(), SyntaxType::OauthScope => v.is_oauthscope(),
SyntaxType::OauthScopeMap => v.is_oauthscopemap() || v.is_refer(), SyntaxType::OauthScopeMap => v.is_oauthscopemap() || v.is_refer(),
SyntaxType::PrivateBinary => v.is_privatebinary(), SyntaxType::PrivateBinary => v.is_privatebinary(),
SyntaxType::IntentToken => matches!(v, PartialValue::IntentToken(_)),
}; };
if r { if r {
Ok(()) Ok(())
@ -235,6 +236,7 @@ impl SchemaAttribute {
SyntaxType::OauthScope => v.is_oauthscope(), SyntaxType::OauthScope => v.is_oauthscope(),
SyntaxType::OauthScopeMap => v.is_oauthscopemap() || v.is_refer(), SyntaxType::OauthScopeMap => v.is_oauthscopemap() || v.is_refer(),
SyntaxType::PrivateBinary => v.is_privatebinary(), SyntaxType::PrivateBinary => v.is_privatebinary(),
SyntaxType::IntentToken => matches!(v, Value::IntentToken(_, _)),
}; };
if r { if r {
Ok(()) Ok(())
@ -281,6 +283,7 @@ impl SchemaAttribute {
SyntaxType::OauthScope => ava.is_oauthscope(), SyntaxType::OauthScope => ava.is_oauthscope(),
SyntaxType::OauthScopeMap => ava.is_oauthscopemap(), SyntaxType::OauthScopeMap => ava.is_oauthscopemap(),
SyntaxType::PrivateBinary => ava.is_privatebinary(), SyntaxType::PrivateBinary => ava.is_privatebinary(),
SyntaxType::IntentToken => ava.is_intenttoken(),
}; };
if valid && ava.validate() { if valid && ava.validate() {
Ok(()) Ok(())

View file

@ -514,6 +514,7 @@ pub trait QueryServerTransaction<'a> {
.ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())), .ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())),
SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())), SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())),
SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())), SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())),
SyntaxType::IntentToken => Err(OperationError::InvalidAttribute("Intent Token Values can not be supplied through modification".to_string())),
} }
} }
None => { None => {
@ -634,6 +635,13 @@ pub trait QueryServerTransaction<'a> {
}), }),
SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)), SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)),
SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary), SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary),
SyntaxType::IntentToken => {
PartialValue::new_intenttoken_s(value).ok_or_else(|| {
OperationError::InvalidAttribute(
"Invalid Intent Token ID (uuid) syntax".to_string(),
)
})
}
} }
} }
None => { None => {
@ -2295,6 +2303,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
JSON_SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE, JSON_SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE,
JSON_SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE, JSON_SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE,
JSON_SCHEMA_ATTR_RS256_PRIVATE_KEY_DER, JSON_SCHEMA_ATTR_RS256_PRIVATE_KEY_DER,
JSON_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN,
JSON_SCHEMA_CLASS_PERSON, JSON_SCHEMA_CLASS_PERSON,
JSON_SCHEMA_CLASS_ORGPERSON, JSON_SCHEMA_CLASS_ORGPERSON,
JSON_SCHEMA_CLASS_GROUP, JSON_SCHEMA_CLASS_GROUP,

View file

@ -8,4 +8,6 @@ mod timings;
pub use event_tag::EventTag; pub use event_tag::EventTag;
pub use middleware::TreeMiddleware; pub use middleware::TreeMiddleware;
pub use subscriber::{main_init, operation_id, test_init, TreePreProcessed, TreeSubscriber}; pub use subscriber::{
main_init, operation_id, test_init, test_level, TreePreProcessed, TreeSubscriber,
};

View file

@ -556,10 +556,27 @@ pub fn main_init() -> JoinHandle<()> {
// and then getting dropped, making the global subscriber panic on further attempts to send logs. // and then getting dropped, making the global subscriber panic on further attempts to send logs.
#[allow(dead_code)] #[allow(dead_code)]
pub fn test_init() -> Result<(), SetGlobalDefaultError> { pub fn test_init() -> Result<(), SetGlobalDefaultError> {
tracing::subscriber::set_global_default(TreeSubscriber { let subscriber = TreeSubscriber {
inner: Registry::default().with(TreeLayer { inner: Registry::default().with(TreeLayer {
fmt: LogFmt::Pretty, fmt: LogFmt::Pretty,
processor: TestProcessor {}, processor: TestProcessor {},
}), }),
}) };
tracing::subscriber::set_global_default(subscriber)
}
#[allow(dead_code)]
pub fn test_level(level: tracing::Level) -> Result<(), SetGlobalDefaultError> {
let subscriber = TreeSubscriber {
inner: Registry::default().with(TreeLayer {
fmt: LogFmt::Pretty,
processor: TestProcessor {},
}),
};
let subscriber =
tracing_subscriber::filter::LevelFilter::from_level(level).with_subscriber(subscriber);
tracing::subscriber::set_global_default(subscriber)
} }

View file

@ -68,6 +68,13 @@ pub enum IndexType {
SubString, SubString,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IntentTokenState {
Valid,
InProgress(Uuid, Duration),
Consumed,
}
impl TryFrom<&str> for IndexType { impl TryFrom<&str> for IndexType {
type Error = (); type Error = ();
@ -154,6 +161,7 @@ pub enum SyntaxType {
OauthScope, OauthScope,
OauthScopeMap, OauthScopeMap,
PrivateBinary, PrivateBinary,
IntentToken,
} }
impl TryFrom<&str> for SyntaxType { impl TryFrom<&str> for SyntaxType {
@ -185,6 +193,7 @@ impl TryFrom<&str> for SyntaxType {
"OAUTH_SCOPE" => Ok(SyntaxType::OauthScope), "OAUTH_SCOPE" => Ok(SyntaxType::OauthScope),
"OAUTH_SCOPE_MAP" => Ok(SyntaxType::OauthScopeMap), "OAUTH_SCOPE_MAP" => Ok(SyntaxType::OauthScopeMap),
"PRIVATE_BINARY" => Ok(SyntaxType::PrivateBinary), "PRIVATE_BINARY" => Ok(SyntaxType::PrivateBinary),
"INTENT_TOKEN" => Ok(SyntaxType::IntentToken),
_ => Err(()), _ => Err(()),
} }
} }
@ -217,6 +226,7 @@ impl TryFrom<usize> for SyntaxType {
19 => Ok(SyntaxType::OauthScope), 19 => Ok(SyntaxType::OauthScope),
20 => Ok(SyntaxType::OauthScopeMap), 20 => Ok(SyntaxType::OauthScopeMap),
21 => Ok(SyntaxType::PrivateBinary), 21 => Ok(SyntaxType::PrivateBinary),
22 => Ok(SyntaxType::IntentToken),
_ => Err(()), _ => Err(()),
} }
} }
@ -247,6 +257,7 @@ impl SyntaxType {
SyntaxType::OauthScope => 19, SyntaxType::OauthScope => 19,
SyntaxType::OauthScopeMap => 20, SyntaxType::OauthScopeMap => 20,
SyntaxType::PrivateBinary => 21, SyntaxType::PrivateBinary => 21,
SyntaxType::IntentToken => 22,
} }
} }
} }
@ -276,6 +287,7 @@ impl fmt::Display for SyntaxType {
SyntaxType::OauthScope => "OAUTH_SCOPE", SyntaxType::OauthScope => "OAUTH_SCOPE",
SyntaxType::OauthScopeMap => "OAUTH_SCOPE_MAP", SyntaxType::OauthScopeMap => "OAUTH_SCOPE_MAP",
SyntaxType::PrivateBinary => "PRIVATE_BINARY", SyntaxType::PrivateBinary => "PRIVATE_BINARY",
SyntaxType::IntentToken => "INTENT_TOKEN",
}) })
} }
} }
@ -320,6 +332,9 @@ pub enum PartialValue {
// Enumeration(String), // Enumeration(String),
// Float64(f64), // Float64(f64),
RestrictedString(String), RestrictedString(String),
IntentToken(Uuid),
TrustedDeviceEnrollment(Uuid),
AuthSession(Uuid),
} }
impl From<SyntaxType> for PartialValue { impl From<SyntaxType> for PartialValue {
@ -631,6 +646,13 @@ impl PartialValue {
PartialValue::RestrictedString(s.to_string()) PartialValue::RestrictedString(s.to_string())
} }
pub fn new_intenttoken_s(us: &str) -> Option<Self> {
match Uuid::parse_str(us) {
Ok(u) => Some(PartialValue::IntentToken(u)),
Err(_) => None,
}
}
pub fn to_str(&self) -> Option<&str> { pub fn to_str(&self) -> Option<&str> {
match self { match self {
PartialValue::Utf8(s) => Some(s.as_str()), PartialValue::Utf8(s) => Some(s.as_str()),
@ -682,6 +704,9 @@ impl PartialValue {
PartialValue::OauthScopeMap(u) => u.to_hyphenated_ref().to_string(), PartialValue::OauthScopeMap(u) => u.to_hyphenated_ref().to_string(),
PartialValue::Address(a) => a.to_string(), PartialValue::Address(a) => a.to_string(),
PartialValue::PhoneNumber(a) => a.to_string(), PartialValue::PhoneNumber(a) => a.to_string(),
PartialValue::IntentToken(u) => u.to_hyphenated_ref().to_string(),
PartialValue::TrustedDeviceEnrollment(u) => u.to_hyphenated_ref().to_string(),
PartialValue::AuthSession(u) => u.to_hyphenated_ref().to_string(),
} }
} }
@ -726,6 +751,9 @@ pub enum Value {
// Enumeration(String), // Enumeration(String),
// Float64(f64), // Float64(f64),
RestrictedString(String), RestrictedString(String),
IntentToken(Uuid, IntentTokenState),
TrustedDeviceEnrollment(Uuid),
AuthSession(Uuid),
} }
impl PartialEq for Value { impl PartialEq for Value {
@ -1411,6 +1439,27 @@ impl Value {
} }
} }
pub fn to_intenttoken(self) -> Option<(Uuid, IntentTokenState)> {
match self {
Value::IntentToken(u, s) => Some((u, s)),
_ => None,
}
}
pub fn to_trusteddeviceenrollment(self) -> Option<(Uuid, ())> {
match self {
Value::TrustedDeviceEnrollment(u) => Some((u, ())),
_ => None,
}
}
pub fn to_authsession(self) -> Option<(Uuid, ())> {
match self {
Value::AuthSession(u) => Some((u, ())),
_ => None,
}
}
pub fn migrate_iutf8_iname(self) -> Option<Self> { pub fn migrate_iutf8_iname(self) -> Option<Self> {
match self { match self {
Value::Iutf8(v) => Some(Value::Iname(v)), Value::Iutf8(v) => Some(Value::Iname(v)),

View file

@ -13,10 +13,10 @@ use time::OffsetDateTime;
use tracing::trace; use tracing::trace;
use crate::be::dbvalue::{ use crate::be::dbvalue::{
DbCidV1, DbValueAddressV1, DbValueCredV1, DbValueEmailAddressV1, DbValueOauthScopeMapV1, DbCidV1, DbValueAddressV1, DbValueCredV1, DbValueEmailAddressV1, DbValueIntentTokenStateV1,
DbValuePhoneNumberV1, DbValueTaggedStringV1, DbValueV1, DbValueOauthScopeMapV1, DbValuePhoneNumberV1, DbValueTaggedStringV1, DbValueV1,
}; };
use crate::value::{Address, INAME_RE, NSUNIQUEID_RE, OAUTHSCOPE_RE}; use crate::value::{Address, IntentTokenState, INAME_RE, NSUNIQUEID_RE, OAUTHSCOPE_RE};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum I { enum I {
@ -34,6 +34,9 @@ enum I {
SecretValue(SmolSet<[String; 1]>), SecretValue(SmolSet<[String; 1]>),
Spn(BTreeSet<(String, String)>), Spn(BTreeSet<(String, String)>),
Uint32(SmolSet<[u32; 1]>), Uint32(SmolSet<[u32; 1]>),
// Uint64(SmolSet<[u64; 1]>),
// Int64(SmolSet<[i64; 1]>),
// Float64(Vec<[f64; 1]>),
Cid(SmolSet<[Cid; 1]>), Cid(SmolSet<[Cid; 1]>),
Nsuniqueid(BTreeSet<String>), Nsuniqueid(BTreeSet<String>),
DateTime(SmolSet<[OffsetDateTime; 1]>), DateTime(SmolSet<[OffsetDateTime; 1]>),
@ -53,9 +56,10 @@ enum I {
OauthScopeMap(BTreeMap<Uuid, BTreeSet<String>>), OauthScopeMap(BTreeMap<Uuid, BTreeSet<String>>),
PrivateBinary(SmolSet<[Vec<u8>; 1]>), PrivateBinary(SmolSet<[Vec<u8>; 1]>),
PublicBinary(BTreeMap<String, Vec<u8>>), PublicBinary(BTreeMap<String, Vec<u8>>),
// Enumeration(SmolSet<[String; 1]>),
// Float64(Vec<[f64; 1]>),
RestrictedString(BTreeSet<String>), RestrictedString(BTreeSet<String>),
IntentToken(BTreeMap<Uuid, IntentTokenState>),
TrustedDeviceEnrollment(BTreeMap<Uuid, ()>),
AuthSession(BTreeMap<Uuid, ()>),
} }
pub struct ValueSet { pub struct ValueSet {
@ -126,6 +130,9 @@ impl From<Value> for ValueSet {
Value::Address(a) => I::Address { set: btreeset![a] }, Value::Address(a) => I::Address { set: btreeset![a] },
Value::PublicBinary(tag, bin) => I::PublicBinary(btreemap![(tag, bin)]), Value::PublicBinary(tag, bin) => I::PublicBinary(btreemap![(tag, bin)]),
Value::RestrictedString(s) => I::RestrictedString(btreeset![s]), Value::RestrictedString(s) => I::RestrictedString(btreeset![s]),
Value::IntentToken(u, t) => I::IntentToken(btreemap![(u, t)]),
Value::TrustedDeviceEnrollment(u) => I::TrustedDeviceEnrollment(btreemap![(u, ())]),
Value::AuthSession(u) => I::AuthSession(btreemap![(u, ())]),
}, },
} }
} }
@ -216,6 +223,20 @@ impl TryFrom<DbValueV1> for ValueSet {
// I::Address { set: btreeset![a] }, // I::Address { set: btreeset![a] },
DbValueV1::PublicBinary(tag, bin) => I::PublicBinary(btreemap![(tag, bin)]), DbValueV1::PublicBinary(tag, bin) => I::PublicBinary(btreemap![(tag, bin)]),
DbValueV1::RestrictedString(s) => I::RestrictedString(btreeset![s]), DbValueV1::RestrictedString(s) => I::RestrictedString(btreeset![s]),
DbValueV1::IntentToken { u, s } => {
let ts = match s {
DbValueIntentTokenStateV1::Valid => IntentTokenState::Valid,
DbValueIntentTokenStateV1::InProgress(pu, pd) => {
IntentTokenState::InProgress(pu, pd)
}
DbValueIntentTokenStateV1::Consumed => IntentTokenState::Consumed,
};
I::IntentToken(btreemap![(u, ts)])
}
DbValueV1::TrustedDeviceEnrollment { u } => {
I::TrustedDeviceEnrollment(btreemap![(u, ())])
}
DbValueV1::AuthSession { u } => I::AuthSession(btreemap![(u, ())]),
}, },
}) })
} }
@ -305,6 +326,30 @@ impl ValueSet {
} }
} }
(I::RestrictedString(set), Value::RestrictedString(s)) => Ok(set.insert(s)), (I::RestrictedString(set), Value::RestrictedString(s)) => Ok(set.insert(s)),
(I::IntentToken(map), Value::IntentToken(u, ts)) => {
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(ts);
Ok(true)
} else {
Ok(false)
}
}
(I::TrustedDeviceEnrollment(map), Value::TrustedDeviceEnrollment(u)) => {
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(());
Ok(true)
} else {
Ok(false)
}
}
(I::AuthSession(map), Value::AuthSession(u)) => {
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(());
Ok(true)
} else {
Ok(false)
}
}
(_, _) => Err(OperationError::InvalidValueState), (_, _) => Err(OperationError::InvalidValueState),
} }
} }
@ -419,6 +464,38 @@ impl ValueSet {
} }
} }
(I::RestrictedString(set), DbValueV1::RestrictedString(s)) => Ok(set.insert(s)), (I::RestrictedString(set), DbValueV1::RestrictedString(s)) => Ok(set.insert(s)),
(I::IntentToken(map), DbValueV1::IntentToken { u, s }) => {
let ts = match s {
DbValueIntentTokenStateV1::Valid => IntentTokenState::Valid,
DbValueIntentTokenStateV1::InProgress(i, d) => {
IntentTokenState::InProgress(i, d)
}
DbValueIntentTokenStateV1::Consumed => IntentTokenState::Consumed,
};
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(ts);
Ok(true)
} else {
Ok(false)
}
}
(I::TrustedDeviceEnrollment(map), DbValueV1::TrustedDeviceEnrollment { u }) => {
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(());
Ok(true)
} else {
Ok(false)
}
}
(I::AuthSession(map), DbValueV1::AuthSession { u }) => {
if let BTreeEntry::Vacant(e) = map.entry(u) {
e.insert(());
Ok(true)
} else {
Ok(false)
}
}
(_, _) => Err(()), (_, _) => Err(()),
} }
.and_then(|is_new| { .and_then(|is_new| {
@ -542,6 +619,15 @@ impl ValueSet {
(I::RestrictedString(a), I::RestrictedString(b)) => { (I::RestrictedString(a), I::RestrictedString(b)) => {
mergesets!(a, b) mergesets!(a, b)
} }
(I::IntentToken(a), I::IntentToken(b)) => {
mergemaps!(a, b)
}
(I::TrustedDeviceEnrollment(a), I::TrustedDeviceEnrollment(b)) => {
mergemaps!(a, b)
}
(I::AuthSession(a), I::AuthSession(b)) => {
mergemaps!(a, b)
}
// I think that in this case, we need to specify self / everything as we are changing // I think that in this case, we need to specify self / everything as we are changing
// type and we need to potentially purge everything, so we just return the left side. // type and we need to potentially purge everything, so we just return the left side.
_ => Err(OperationError::InvalidValueState), _ => Err(OperationError::InvalidValueState),
@ -652,6 +738,15 @@ impl ValueSet {
I::RestrictedString(set) => { I::RestrictedString(set) => {
set.extend(iter.filter_map(|v| v.to_restrictedstring())); set.extend(iter.filter_map(|v| v.to_restrictedstring()));
} }
I::IntentToken(map) => {
map.extend(iter.filter_map(|v| v.to_intenttoken()));
}
I::TrustedDeviceEnrollment(map) => {
map.extend(iter.filter_map(|v| v.to_trusteddeviceenrollment()));
}
I::AuthSession(map) => {
map.extend(iter.filter_map(|v| v.to_authsession()));
}
} }
} }
@ -737,6 +832,15 @@ impl ValueSet {
I::RestrictedString(set) => { I::RestrictedString(set) => {
set.clear(); set.clear();
} }
I::IntentToken(map) => {
map.clear();
}
I::TrustedDeviceEnrollment(map) => {
map.clear();
}
I::AuthSession(map) => {
map.clear();
}
}; };
debug_assert!(self.is_empty()); debug_assert!(self.is_empty());
} }
@ -831,6 +935,15 @@ impl ValueSet {
(I::RestrictedString(set), PartialValue::RestrictedString(s)) => { (I::RestrictedString(set), PartialValue::RestrictedString(s)) => {
set.remove(s); set.remove(s);
} }
(I::IntentToken(map), PartialValue::IntentToken(t)) => {
map.remove(t);
}
(I::TrustedDeviceEnrollment(map), PartialValue::TrustedDeviceEnrollment(t)) => {
map.remove(t);
}
(I::AuthSession(map), PartialValue::AuthSession(t)) => {
map.remove(t);
}
(_, _) => { (_, _) => {
debug_assert!(false) debug_assert!(false)
} }
@ -873,6 +986,11 @@ impl ValueSet {
(I::RestrictedString(set), PartialValue::RestrictedString(s)) => { (I::RestrictedString(set), PartialValue::RestrictedString(s)) => {
set.contains(s.as_str()) set.contains(s.as_str())
} }
(I::IntentToken(map), PartialValue::IntentToken(u)) => map.contains_key(u),
(I::TrustedDeviceEnrollment(map), PartialValue::TrustedDeviceEnrollment(u)) => {
map.contains_key(u)
}
(I::AuthSession(map), PartialValue::AuthSession(u)) => map.contains_key(u),
_ => false, _ => false,
} }
} }
@ -925,6 +1043,9 @@ impl ValueSet {
I::PrivateBinary(set) => set.len(), I::PrivateBinary(set) => set.len(),
I::PublicBinary(map) => map.len(), I::PublicBinary(map) => map.len(),
I::RestrictedString(set) => set.len(), I::RestrictedString(set) => set.len(),
I::IntentToken(map) => map.len(),
I::TrustedDeviceEnrollment(map) => map.len(),
I::AuthSession(map) => map.len(),
} }
} }
@ -986,6 +1107,18 @@ impl ValueSet {
I::PrivateBinary(_set) => vec![], I::PrivateBinary(_set) => vec![],
I::PublicBinary(map) => map.keys().cloned().collect(), I::PublicBinary(map) => map.keys().cloned().collect(),
I::RestrictedString(set) => set.iter().cloned().collect(), I::RestrictedString(set) => set.iter().cloned().collect(),
I::IntentToken(map) => map
.keys()
.map(|u| u.to_hyphenated_ref().to_string())
.collect(),
I::TrustedDeviceEnrollment(map) => map
.keys()
.map(|u| u.to_hyphenated_ref().to_string())
.collect(),
I::AuthSession(map) => map
.keys()
.map(|u| u.to_hyphenated_ref().to_string())
.collect(),
} }
} }
@ -1263,6 +1396,48 @@ impl ValueSet {
}) })
} }
} }
(I::IntentToken(a), I::IntentToken(b)) => {
let x: BTreeMap<_, _> = a
.iter()
.filter(|(k, _)| b.contains_key(k))
.map(|(k, v)| (*k, v.clone()))
.collect();
if x.is_empty() {
None
} else {
Some(ValueSet {
inner: I::IntentToken(x),
})
}
}
(I::TrustedDeviceEnrollment(a), I::TrustedDeviceEnrollment(b)) => {
let x: BTreeMap<_, _> = a
.iter()
.filter(|(k, _)| b.contains_key(k))
.map(|(k, v)| (*k, v.clone()))
.collect();
if x.is_empty() {
None
} else {
Some(ValueSet {
inner: I::TrustedDeviceEnrollment(x),
})
}
}
(I::AuthSession(a), I::AuthSession(b)) => {
let x: BTreeMap<_, _> = a
.iter()
.filter(|(k, _)| b.contains_key(k))
.map(|(k, v)| (*k, v.clone()))
.collect();
if x.is_empty() {
None
} else {
Some(ValueSet {
inner: I::AuthSession(x),
})
}
}
// I think that in this case, we need to specify self / everything as we are changing // I think that in this case, we need to specify self / everything as we are changing
// type and we need to potentially purge everything, so we just return the left side. // type and we need to potentially purge everything, so we just return the left side.
_ => Some(self.clone()), _ => Some(self.clone()),
@ -1550,6 +1725,13 @@ impl ValueSet {
} }
} }
pub fn as_intenttoken(&self) -> Option<&BTreeMap<Uuid, IntentTokenState>> {
match &self.inner {
I::IntentToken(map) => Some(map),
_ => None,
}
}
pub fn to_proto_string_clone_iter(&self) -> ProtoIter<'_> { pub fn to_proto_string_clone_iter(&self) -> ProtoIter<'_> {
// to_proto_string_clone // to_proto_string_clone
match &self.inner { match &self.inner {
@ -1579,6 +1761,9 @@ impl ValueSet {
I::PrivateBinary(set) => ProtoIter::PrivateBinary(set.iter()), I::PrivateBinary(set) => ProtoIter::PrivateBinary(set.iter()),
I::PublicBinary(set) => ProtoIter::PublicBinary(set.iter()), I::PublicBinary(set) => ProtoIter::PublicBinary(set.iter()),
I::RestrictedString(set) => ProtoIter::RestrictedString(set.iter()), I::RestrictedString(set) => ProtoIter::RestrictedString(set.iter()),
I::IntentToken(map) => ProtoIter::IntentToken(map.iter()),
I::TrustedDeviceEnrollment(map) => ProtoIter::TrustedDeviceEnrollment(map.iter()),
I::AuthSession(map) => ProtoIter::AuthSession(map.iter()),
} }
} }
@ -1614,6 +1799,9 @@ impl ValueSet {
I::PrivateBinary(set) => DbValueV1Iter::PrivateBinary(set.iter()), I::PrivateBinary(set) => DbValueV1Iter::PrivateBinary(set.iter()),
I::PublicBinary(set) => DbValueV1Iter::PublicBinary(set.iter()), I::PublicBinary(set) => DbValueV1Iter::PublicBinary(set.iter()),
I::RestrictedString(set) => DbValueV1Iter::RestrictedString(set.iter()), I::RestrictedString(set) => DbValueV1Iter::RestrictedString(set.iter()),
I::IntentToken(map) => DbValueV1Iter::IntentToken(map.iter()),
I::TrustedDeviceEnrollment(map) => DbValueV1Iter::TrustedDeviceEnrollment(map.iter()),
I::AuthSession(map) => DbValueV1Iter::AuthSession(map.iter()),
} }
} }
@ -1645,6 +1833,11 @@ impl ValueSet {
I::PrivateBinary(set) => PartialValueIter::PrivateBinary(set.iter()), I::PrivateBinary(set) => PartialValueIter::PrivateBinary(set.iter()),
I::PublicBinary(set) => PartialValueIter::PublicBinary(set.iter()), I::PublicBinary(set) => PartialValueIter::PublicBinary(set.iter()),
I::RestrictedString(set) => PartialValueIter::RestrictedString(set.iter()), I::RestrictedString(set) => PartialValueIter::RestrictedString(set.iter()),
I::IntentToken(map) => PartialValueIter::IntentToken(map.iter()),
I::TrustedDeviceEnrollment(map) => {
PartialValueIter::TrustedDeviceEnrollment(map.iter())
}
I::AuthSession(map) => PartialValueIter::AuthSession(map.iter()),
} }
} }
@ -1676,6 +1869,9 @@ impl ValueSet {
I::PrivateBinary(set) => ValueIter::PrivateBinary(set.iter()), I::PrivateBinary(set) => ValueIter::PrivateBinary(set.iter()),
I::PublicBinary(set) => ValueIter::PublicBinary(set.iter()), I::PublicBinary(set) => ValueIter::PublicBinary(set.iter()),
I::RestrictedString(set) => ValueIter::RestrictedString(set.iter()), I::RestrictedString(set) => ValueIter::RestrictedString(set.iter()),
I::IntentToken(map) => ValueIter::IntentToken(map.iter()),
I::TrustedDeviceEnrollment(map) => ValueIter::TrustedDeviceEnrollment(map.iter()),
I::AuthSession(map) => ValueIter::AuthSession(map.iter()),
} }
} }
@ -1808,6 +2004,10 @@ impl ValueSet {
matches!(self.inner, I::PrivateBinary(_)) matches!(self.inner, I::PrivateBinary(_))
} }
pub fn is_intenttoken(&self) -> bool {
matches!(self.inner, I::IntentToken(_))
}
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
match &self.inner { match &self.inner {
I::Iname(set) => set.iter().all(|s| { I::Iname(set) => set.iter().all(|s| {
@ -1906,6 +2106,9 @@ impl PartialEq for ValueSet {
(I::PrivateBinary(a), I::PrivateBinary(b)) => a.eq(b), (I::PrivateBinary(a), I::PrivateBinary(b)) => a.eq(b),
(I::PublicBinary(a), I::PublicBinary(b)) => a.eq(b), (I::PublicBinary(a), I::PublicBinary(b)) => a.eq(b),
(I::RestrictedString(a), I::RestrictedString(b)) => a.eq(b), (I::RestrictedString(a), I::RestrictedString(b)) => a.eq(b),
(I::IntentToken(a), I::IntentToken(b)) => a.eq(b),
(I::TrustedDeviceEnrollment(a), I::TrustedDeviceEnrollment(b)) => a.eq(b),
(I::AuthSession(a), I::AuthSession(b)) => a.eq(b),
_ => false, _ => false,
} }
} }
@ -1973,6 +2176,9 @@ pub enum ValueIter<'a> {
PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>), PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>),
PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>), PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>),
RestrictedString(std::collections::btree_set::Iter<'a, String>), RestrictedString(std::collections::btree_set::Iter<'a, String>),
IntentToken(std::collections::btree_map::Iter<'a, Uuid, IntentTokenState>),
TrustedDeviceEnrollment(std::collections::btree_map::Iter<'a, Uuid, ()>),
AuthSession(std::collections::btree_map::Iter<'a, Uuid, ()>),
} }
impl<'a> Iterator for ValueIter<'a> { impl<'a> Iterator for ValueIter<'a> {
@ -2032,6 +2238,13 @@ impl<'a> Iterator for ValueIter<'a> {
ValueIter::RestrictedString(iter) => { ValueIter::RestrictedString(iter) => {
iter.next().map(|i| Value::new_restrictedstring(i.clone())) iter.next().map(|i| Value::new_restrictedstring(i.clone()))
} }
ValueIter::IntentToken(iter) => iter
.next()
.map(|(u, ts)| Value::IntentToken(*u, ts.clone())),
ValueIter::TrustedDeviceEnrollment(iter) => {
iter.next().map(|(u, _)| Value::TrustedDeviceEnrollment(*u))
}
ValueIter::AuthSession(iter) => iter.next().map(|(u, _)| Value::AuthSession(*u)),
} }
} }
} }
@ -2063,6 +2276,9 @@ pub enum PartialValueIter<'a> {
PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>), PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>),
PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>), PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>),
RestrictedString(std::collections::btree_set::Iter<'a, String>), RestrictedString(std::collections::btree_set::Iter<'a, String>),
IntentToken(std::collections::btree_map::Iter<'a, Uuid, IntentTokenState>),
TrustedDeviceEnrollment(std::collections::btree_map::Iter<'a, Uuid, ()>),
AuthSession(std::collections::btree_map::Iter<'a, Uuid, ()>),
} }
impl<'a> Iterator for PartialValueIter<'a> { impl<'a> Iterator for PartialValueIter<'a> {
@ -2135,6 +2351,15 @@ impl<'a> Iterator for PartialValueIter<'a> {
PartialValueIter::RestrictedString(iter) => iter PartialValueIter::RestrictedString(iter) => iter
.next() .next()
.map(|i| PartialValue::new_restrictedstring_s(i.as_str())), .map(|i| PartialValue::new_restrictedstring_s(i.as_str())),
PartialValueIter::IntentToken(iter) => {
iter.next().map(|(u, _state)| PartialValue::IntentToken(*u))
}
PartialValueIter::TrustedDeviceEnrollment(iter) => iter
.next()
.map(|(u, _)| PartialValue::TrustedDeviceEnrollment(*u)),
PartialValueIter::AuthSession(iter) => {
iter.next().map(|(u, _)| PartialValue::AuthSession(*u))
}
} }
} }
} }
@ -2172,6 +2397,9 @@ pub enum DbValueV1Iter<'a> {
PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>), PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>),
PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>), PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>),
RestrictedString(std::collections::btree_set::Iter<'a, String>), RestrictedString(std::collections::btree_set::Iter<'a, String>),
IntentToken(std::collections::btree_map::Iter<'a, Uuid, IntentTokenState>),
TrustedDeviceEnrollment(std::collections::btree_map::Iter<'a, Uuid, ()>),
AuthSession(std::collections::btree_map::Iter<'a, Uuid, ()>),
} }
impl<'a> Iterator for DbValueV1Iter<'a> { impl<'a> Iterator for DbValueV1Iter<'a> {
@ -2267,6 +2495,22 @@ impl<'a> Iterator for DbValueV1Iter<'a> {
DbValueV1Iter::RestrictedString(iter) => { DbValueV1Iter::RestrictedString(iter) => {
iter.next().map(|i| DbValueV1::RestrictedString(i.clone())) iter.next().map(|i| DbValueV1::RestrictedString(i.clone()))
} }
DbValueV1Iter::IntentToken(iter) => iter.next().map(|(u, s)| DbValueV1::IntentToken {
u: *u,
s: match s {
IntentTokenState::Valid => DbValueIntentTokenStateV1::Valid,
IntentTokenState::InProgress(i, d) => {
DbValueIntentTokenStateV1::InProgress(*i, *d)
}
IntentTokenState::Consumed => DbValueIntentTokenStateV1::Consumed,
},
}),
DbValueV1Iter::TrustedDeviceEnrollment(iter) => iter
.next()
.map(|(u, _)| DbValueV1::TrustedDeviceEnrollment { u: *u }),
DbValueV1Iter::AuthSession(iter) => {
iter.next().map(|(u, _)| DbValueV1::AuthSession { u: *u })
}
} }
} }
} }
@ -2298,6 +2542,9 @@ pub enum ProtoIter<'a> {
PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>), PrivateBinary(SmolSetIter<'a, [Vec<u8>; 1]>),
PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>), PublicBinary(std::collections::btree_map::Iter<'a, String, Vec<u8>>),
RestrictedString(std::collections::btree_set::Iter<'a, String>), RestrictedString(std::collections::btree_set::Iter<'a, String>),
IntentToken(std::collections::btree_map::Iter<'a, Uuid, IntentTokenState>),
TrustedDeviceEnrollment(std::collections::btree_map::Iter<'a, Uuid, ()>),
AuthSession(std::collections::btree_map::Iter<'a, Uuid, ()>),
} }
impl<'a> Iterator for ProtoIter<'a> { impl<'a> Iterator for ProtoIter<'a> {
@ -2357,6 +2604,15 @@ impl<'a> Iterator for ProtoIter<'a> {
.next() .next()
.map(|(t, b)| format!("{}: {}", t, base64::encode_config(&b, base64::URL_SAFE))), .map(|(t, b)| format!("{}: {}", t, base64::encode_config(&b, base64::URL_SAFE))),
ProtoIter::RestrictedString(iter) => iter.next().cloned(), ProtoIter::RestrictedString(iter) => iter.next().cloned(),
ProtoIter::IntentToken(iter) => iter
.next()
.map(|(u, m)| format!("{}: {:?}", ValueSet::uuid_to_proto_string(u), m)),
ProtoIter::TrustedDeviceEnrollment(iter) => iter
.next()
.map(|(u, _)| format!("{}", ValueSet::uuid_to_proto_string(u))),
ProtoIter::AuthSession(iter) => iter
.next()
.map(|(u, _)| format!("{}", ValueSet::uuid_to_proto_string(u))),
} }
} }
} }

View file

@ -13,7 +13,7 @@ repository = "https://github.com/kanidm/kanidm/"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
kanidm = { path = "../" } kanidm = { path = "../idm" }
kanidm_proto = { path = "../../kanidm_proto" } kanidm_proto = { path = "../../kanidm_proto" }
libc = "0.2" libc = "0.2"
@ -37,3 +37,21 @@ compact_jwt = "^0.2.0"
[build-dependencies] [build-dependencies]
profiles = { path = "../../profiles" } profiles = { path = "../../profiles" }
[dev-dependencies]
tracing-subscriber = "0.3"
# tokio = { version = "1", features = ["rt", "net", "time", "macros", "sync", "signal"] }
# kanidm = { path = "../kanidmd" }
# score = { path = "../kanidmd/score" }
futures = "0.3"
serde_json = "1.0"
# async-std = { version = "1.6", features = ["tokio1"] }
webauthn-authenticator-rs = "0.3.0-alpha.12"
oauth2_ext = { package = "oauth2", version = "4.0", default-features = false }
base64 = "0.13"
# compact_jwt = "^0.1.5"
kanidm_client = { path = "../../kanidm_client" }
url = { version = "2", features = ["serde"] }
reqwest = { version = "0.11", features=["cookies", "json", "native-tls"] }

View file

@ -374,27 +374,45 @@ fn test_server_radius_credential_lifecycle() {
.idm_account_person_extend("admin", None, None) .idm_account_person_extend("admin", None, None)
.unwrap(); .unwrap();
let f = Filter::Eq("name".to_string(), "idm_admins".to_string());
let m = ModifyList::new_list(vec![Modify::Present(
"member".to_string(),
"system_admins".to_string(),
)]);
let res = rsclient.modify(f, m);
assert!(res.is_ok());
rsclient
.idm_account_create("demo_account", "Deeeeemo")
.unwrap();
// Should have no radius secret // Should have no radius secret
let n_sec = rsclient.idm_account_radius_credential_get("admin").unwrap(); let n_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.unwrap();
assert!(n_sec.is_none()); assert!(n_sec.is_none());
// Set one // Set one
let sec1 = rsclient let sec1 = rsclient
.idm_account_radius_credential_regenerate("admin") .idm_account_radius_credential_regenerate("demo_account")
.unwrap(); .unwrap();
// Should be able to get it. // Should be able to get it.
let r_sec = rsclient.idm_account_radius_credential_get("admin").unwrap(); let r_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.unwrap();
assert!(sec1 == r_sec.unwrap()); assert!(sec1 == r_sec.unwrap());
// test getting the token - we can do this as self or the radius server // test getting the token - we can do this as self or the radius server
let r_tok = rsclient.idm_account_radius_token_get("admin").unwrap(); let r_tok = rsclient
.idm_account_radius_token_get("demo_account")
.unwrap();
assert!(sec1 == r_tok.secret); assert!(sec1 == r_tok.secret);
assert!(r_tok.name == "admin"); assert!(r_tok.name == "demo_account");
// Reset it // Reset it
let sec2 = rsclient let sec2 = rsclient
.idm_account_radius_credential_regenerate("admin") .idm_account_radius_credential_regenerate("demo_account")
.unwrap(); .unwrap();
// Should be different // Should be different
@ -402,11 +420,13 @@ fn test_server_radius_credential_lifecycle() {
assert!(sec1 != sec2); assert!(sec1 != sec2);
// Delete it // Delete it
let res = rsclient.idm_account_radius_credential_delete("admin"); let res = rsclient.idm_account_radius_credential_delete("demo_account");
assert!(res.is_ok()); assert!(res.is_ok());
// No secret // No secret
let n_sec = rsclient.idm_account_radius_credential_get("admin").unwrap(); let n_sec = rsclient
.idm_account_radius_credential_get("demo_account")
.unwrap();
assert!(n_sec.is_none()); assert!(n_sec.is_none());
}); });
} }

31
kanidmd_web_ui/pkg/kanidmd_web_ui.d.ts vendored Normal file
View file

@ -0,0 +1,31 @@
/* tslint:disable */
/* eslint-disable */
/**
*/
export function run_app(): void;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly run_app: (a: number) => void;
readonly __wbindgen_malloc: (a: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;
readonly _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h79af8eb94e22cb43: (a: number, b: number, c: number) => void;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h1c17030d49aa8a7b: (a: number, b: number, c: number) => void;
readonly _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb210817f7291cc2b: (a: number, b: number, c: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_free: (a: number, b: number) => void;
readonly __wbindgen_exn_store: (a: number) => void;
}
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

View file

@ -7,32 +7,7 @@ heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; } function getObject(idx) { return heap[idx]; }
let heap_next = heap.length; let WASM_VECTOR_LEN = 0;
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
let cachegetUint8Memory0 = null; let cachegetUint8Memory0 = null;
function getUint8Memory0() { function getUint8Memory0() {
@ -42,12 +17,6 @@ function getUint8Memory0() {
return cachegetUint8Memory0; return cachegetUint8Memory0;
} }
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let WASM_VECTOR_LEN = 0;
let cachedTextEncoder = new TextEncoder('utf-8'); let cachedTextEncoder = new TextEncoder('utf-8');
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
@ -65,6 +34,8 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
function passStringToWasm0(arg, malloc, realloc) { function passStringToWasm0(arg, malloc, realloc) {
if (typeof(arg) !== 'string') throw new Error('expected a string argument');
if (realloc === undefined) { if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg); const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length); const ptr = malloc(buf.length);
@ -93,7 +64,7 @@ function passStringToWasm0(arg, malloc, realloc) {
ptr = realloc(ptr, len, len = offset + arg.length * 3); ptr = realloc(ptr, len, len = offset + arg.length * 3);
const view = getUint8Memory0().subarray(ptr + offset, ptr + len); const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view); const ret = encodeString(arg, view);
if (ret.read !== arg.length) throw new Error('failed to pass whole string');
offset += ret.written; offset += ret.written;
} }
@ -101,10 +72,6 @@ function passStringToWasm0(arg, malloc, realloc) {
return ptr; return ptr;
} }
function isLikeNone(x) {
return x === undefined || x === null;
}
let cachegetInt32Memory0 = null; let cachegetInt32Memory0 = null;
function getInt32Memory0() { function getInt32Memory0() {
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
@ -113,6 +80,41 @@ function getInt32Memory0() {
return cachegetInt32Memory0; return cachegetInt32Memory0;
} }
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
function getStringFromWasm0(ptr, len) {
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
}
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
if (typeof(heap_next) !== 'number') throw new Error('corrupt heap');
heap[idx] = obj;
return idx;
}
function isLikeNone(x) {
return x === undefined || x === null;
}
function _assertNum(n) {
if (typeof(n) !== 'number') throw new Error('expected a number argument');
}
function _assertBoolean(n) {
if (typeof(n) !== 'boolean') {
throw new Error('expected a boolean argument');
}
}
let cachegetFloat64Memory0 = null; let cachegetFloat64Memory0 = null;
function getFloat64Memory0() { function getFloat64Memory0() {
if (cachegetFloat64Memory0 === null || cachegetFloat64Memory0.buffer !== wasm.memory.buffer) { if (cachegetFloat64Memory0 === null || cachegetFloat64Memory0.buffer !== wasm.memory.buffer) {
@ -121,6 +123,18 @@ function getFloat64Memory0() {
return cachegetFloat64Memory0; return cachegetFloat64Memory0;
} }
function dropObject(idx) {
if (idx < 36) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
function debugString(val) { function debugString(val) {
// primitive types // primitive types
const type = typeof val; const type = typeof val;
@ -186,6 +200,49 @@ function debugString(val) {
return className; return className;
} }
function makeClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => {
// First up with a closure we increment the internal reference
// count. This ensures that the Rust closure environment won't
// be deallocated while we're invoking it.
state.cnt++;
try {
return f(state.a, state.b, ...args);
} finally {
if (--state.cnt === 0) {
wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b);
state.a = 0;
}
}
};
real.original = state;
return real;
}
function logError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
let error = (function () {
try {
return e instanceof Error ? `${e.message}\n\nStack:\n${e.stack}` : e.toString();
} catch(_) {
return "<failed to stringify thrown value>";
}
}());
console.error("wasm-bindgen: imported JS function that was not marked as `catch` threw an error:", error);
throw e;
}
}
function __wbg_adapter_30(arg0, arg1, arg2) {
_assertNum(arg0);
_assertNum(arg1);
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h79af8eb94e22cb43(arg0, arg1, addHeapObject(arg2));
}
function makeMutClosure(arg0, arg1, dtor, f) { function makeMutClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor }; const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => { const real = (...args) => {
@ -210,6 +267,11 @@ function makeMutClosure(arg0, arg1, dtor, f) {
return real; return real;
} }
function __wbg_adapter_33(arg0, arg1, arg2) {
_assertNum(arg0);
_assertNum(arg1);
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h1c17030d49aa8a7b(arg0, arg1, addHeapObject(arg2));
}
let stack_pointer = 32; let stack_pointer = 32;
@ -218,43 +280,16 @@ function addBorrowedObject(obj) {
heap[--stack_pointer] = obj; heap[--stack_pointer] = obj;
return stack_pointer; return stack_pointer;
} }
function __wbg_adapter_30(arg0, arg1, arg2) { function __wbg_adapter_36(arg0, arg1, arg2) {
try { try {
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h044f18bcb347ffd0(arg0, arg1, addBorrowedObject(arg2)); _assertNum(arg0);
_assertNum(arg1);
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb210817f7291cc2b(arg0, arg1, addBorrowedObject(arg2));
} finally { } finally {
heap[stack_pointer++] = undefined; heap[stack_pointer++] = undefined;
} }
} }
function makeClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => {
// First up with a closure we increment the internal reference
// count. This ensures that the Rust closure environment won't
// be deallocated while we're invoking it.
state.cnt++;
try {
return f(state.a, state.b, ...args);
} finally {
if (--state.cnt === 0) {
wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b);
state.a = 0;
}
}
};
real.original = state;
return real;
}
function __wbg_adapter_33(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha821c5ee02284a59(arg0, arg1, addHeapObject(arg2));
}
function __wbg_adapter_36(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb14a7cff63e25e7d(arg0, arg1, addHeapObject(arg2));
}
/** /**
*/ */
export function run_app() { export function run_app() {
@ -334,12 +369,13 @@ async function init(input) {
} }
const imports = {}; const imports = {};
imports.wbg = {}; imports.wbg = {};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) { imports.wbg.__wbindgen_json_serialize = function(arg0, arg1) {
takeObject(arg0); const obj = getObject(arg1);
}; var ret = JSON.stringify(obj === undefined ? null : obj);
imports.wbg.__wbindgen_object_clone_ref = function(arg0) { var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var ret = getObject(arg0); var len0 = WASM_VECTOR_LEN;
return addHeapObject(ret); getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
}; };
imports.wbg.__wbindgen_string_new = function(arg0, arg1) { imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
var ret = getStringFromWasm0(arg0, arg1); var ret = getStringFromWasm0(arg0, arg1);
@ -353,36 +389,25 @@ async function init(input) {
getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0; getInt32Memory0()[arg0 / 4 + 0] = ptr0;
}; };
imports.wbg.__wbindgen_json_serialize = function(arg0, arg1) { imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
const obj = getObject(arg1); var ret = getObject(arg0);
var ret = JSON.stringify(obj === undefined ? null : obj); return addHeapObject(ret);
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbindgen_cb_drop = function(arg0) {
const obj = takeObject(arg0).original;
if (obj.cnt-- == 1) {
obj.a = 0;
return true;
}
var ret = false;
return ret;
};
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
var ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
return ret;
};
imports.wbg.__wbindgen_is_undefined = function(arg0) {
var ret = getObject(arg0) === undefined;
return ret;
}; };
imports.wbg.__wbindgen_json_parse = function(arg0, arg1) { imports.wbg.__wbindgen_json_parse = function(arg0, arg1) {
var ret = JSON.parse(getStringFromWasm0(arg0, arg1)); var ret = JSON.parse(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
var ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
_assertNum(ret);
return ret;
};
imports.wbg.__wbindgen_is_undefined = function(arg0) {
var ret = getObject(arg0) === undefined;
_assertBoolean(ret);
return ret;
};
imports.wbg.__wbindgen_number_new = function(arg0) { imports.wbg.__wbindgen_number_new = function(arg0) {
var ret = arg0; var ret = arg0;
return addHeapObject(ret); return addHeapObject(ret);
@ -390,57 +415,74 @@ async function init(input) {
imports.wbg.__wbindgen_number_get = function(arg0, arg1) { imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
const obj = getObject(arg1); const obj = getObject(arg1);
var ret = typeof(obj) === 'number' ? obj : undefined; var ret = typeof(obj) === 'number' ? obj : undefined;
if (!isLikeNone(ret)) {
_assertNum(ret);
}
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.__wbg_new_693216e109162396 = function() { imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
var ret = new Error(); takeObject(arg0);
return addHeapObject(ret);
}; };
imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) { imports.wbg.__wbg_error_09919627ac0992f5 = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).stack;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {
try { try {
console.error(getStringFromWasm0(arg0, arg1)); console.error(getStringFromWasm0(arg0, arg1));
} finally { } finally {
wasm.__wbindgen_free(arg0, arg1); wasm.__wbindgen_free(arg0, arg1);
} }
}, arguments) };
imports.wbg.__wbg_new_693216e109162396 = function() { return logError(function () {
var ret = new Error();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).stack;
var ptr0 = 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.__wbindgen_cb_drop = function(arg0) {
const obj = takeObject(arg0).original;
if (obj.cnt-- == 1) {
obj.a = 0;
return true;
}
var ret = false;
_assertBoolean(ret);
return ret;
}; };
imports.wbg.__wbg_log_06b7ffc63a0f8bee = function(arg0, arg1) { imports.wbg.__wbg_log_06b7ffc63a0f8bee = function() { return logError(function (arg0, arg1) {
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
wasm.__wbindgen_free(arg0, arg1 * 4); wasm.__wbindgen_free(arg0, arg1 * 4);
console.log(...v0); console.log(...v0);
}; }, arguments) };
imports.wbg.__wbg_warn_2aa0e7178e1d35f6 = function(arg0, arg1) { imports.wbg.__wbg_warn_2aa0e7178e1d35f6 = function() { return logError(function (arg0, arg1) {
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
wasm.__wbindgen_free(arg0, arg1 * 4); wasm.__wbindgen_free(arg0, arg1 * 4);
console.warn(...v0); console.warn(...v0);
}; }, arguments) };
imports.wbg.__wbg_instanceof_Window_434ce1849eb4e0fc = function(arg0) { imports.wbg.__wbg_instanceof_Window_434ce1849eb4e0fc = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof Window; var ret = getObject(arg0) instanceof Window;
_assertBoolean(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_document_5edd43643d1060d9 = function(arg0) { imports.wbg.__wbg_document_5edd43643d1060d9 = function() { return logError(function (arg0) {
var ret = getObject(arg0).document; var ret = getObject(arg0).document;
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_location_11472bb76bf5bbca = function(arg0) { imports.wbg.__wbg_location_11472bb76bf5bbca = function() { return logError(function (arg0) {
var ret = getObject(arg0).location; var ret = getObject(arg0).location;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_history_52cfc93c824e772b = function() { return handleError(function (arg0) { imports.wbg.__wbg_history_52cfc93c824e772b = function() { return handleError(function (arg0) {
var ret = getObject(arg0).history; var ret = getObject(arg0).history;
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_navigator_0e0588c949560476 = function(arg0) { imports.wbg.__wbg_navigator_0e0588c949560476 = function() { return logError(function (arg0) {
var ret = getObject(arg0).navigator; var ret = getObject(arg0).navigator;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_localStorage_2b7091e6919605e2 = function() { return handleError(function (arg0) { imports.wbg.__wbg_localStorage_2b7091e6919605e2 = function() { return handleError(function (arg0) {
var ret = getObject(arg0).localStorage; var ret = getObject(arg0).localStorage;
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
@ -449,14 +491,14 @@ async function init(input) {
var ret = getObject(arg0).sessionStorage; var ret = getObject(arg0).sessionStorage;
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_fetch_427498e0ccea81f4 = function(arg0, arg1) { imports.wbg.__wbg_fetch_427498e0ccea81f4 = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg0).fetch(getObject(arg1)); var ret = getObject(arg0).fetch(getObject(arg1));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_body_7538539844356c1c = function(arg0) { imports.wbg.__wbg_body_7538539844356c1c = function() { return logError(function (arg0) {
var ret = getObject(arg0).body; var ret = getObject(arg0).body;
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_createElement_d017b8d2af99bab9 = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_createElement_d017b8d2af99bab9 = function() { return handleError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2)); var ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2));
return addHeapObject(ret); return addHeapObject(ret);
@ -465,89 +507,102 @@ async function init(input) {
var ret = getObject(arg0).createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); var ret = getObject(arg0).createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_createTextNode_39a0de25d14bcde5 = function(arg0, arg1, arg2) { imports.wbg.__wbg_createTextNode_39a0de25d14bcde5 = function() { return logError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2)); var ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_getElementById_b30e88aff96f66a1 = function(arg0, arg1, arg2) { imports.wbg.__wbg_getElementById_b30e88aff96f66a1 = function() { return logError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2)); var ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2));
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_querySelector_cc714d0aa0b868ed = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_querySelector_cc714d0aa0b868ed = function() { return handleError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2)); var ret = getObject(arg0).querySelector(getStringFromWasm0(arg1, arg2));
return isLikeNone(ret) ? 0 : addHeapObject(ret); return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_pathname_7affbcff36f35c0e = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).pathname;
var ptr0 = 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_new_4473c9af1cac368b = function() { return handleError(function (arg0, arg1) {
var ret = new URL(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_value_d3a30bc2c7caf357 = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).value;
var ptr0 = 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_setvalue_6a34bab301f38bf2 = function() { return logError(function (arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
}, arguments) };
imports.wbg.__wbg_get_5835a17331a9d8f2 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).get(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_add_c1e566b20be6badb = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_add_c1e566b20be6badb = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).add(getStringFromWasm0(arg1, arg2)); getObject(arg0).add(getStringFromWasm0(arg1, arg2));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_remove_b4d29ca5eb7db54e = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_remove_b4d29ca5eb7db54e = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).remove(getStringFromWasm0(arg1, arg2)); getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_href_cad8f02caf39f2fb = function(arg0, arg1) { imports.wbg.__wbg_href_cad8f02caf39f2fb = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).href; var ret = getObject(arg1).href;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN; var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0; getInt32Memory0()[arg0 / 4 + 0] = ptr0;
}; }, arguments) };
imports.wbg.__wbg_getClientExtensionResults_37549795564cd9d3 = function(arg0) { imports.wbg.__wbg_headers_1a60dec7fbd28a3b = function() { return logError(function (arg0) {
var ret = getObject(arg0).getClientExtensionResults();
return addHeapObject(ret);
};
imports.wbg.__wbg_headers_1a60dec7fbd28a3b = function(arg0) {
var ret = getObject(arg0).headers; var ret = getObject(arg0).headers;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_newwithstrandinit_c07f0662ece15bc6 = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_newwithstrandinit_c07f0662ece15bc6 = function() { return handleError(function (arg0, arg1, arg2) {
var ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2)); var ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_instanceof_HtmlInputElement_8969541a2a0bded0 = function(arg0) { imports.wbg.__wbg_instanceof_Event_39e54e1fe6593f4c = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof HTMLInputElement; var ret = getObject(arg0) instanceof Event;
_assertBoolean(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_setchecked_f6ead3490df88a7f = function(arg0, arg1) { imports.wbg.__wbg_target_e560052e31e4567c = function() { return logError(function (arg0) {
var ret = getObject(arg0).target;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_cancelBubble_17d7988ab2fbe4c9 = function() { return logError(function (arg0) {
var ret = getObject(arg0).cancelBubble;
_assertBoolean(ret);
return ret;
}, arguments) };
imports.wbg.__wbg_preventDefault_fa00541ff125b78c = function() { return logError(function (arg0) {
getObject(arg0).preventDefault();
}, arguments) };
imports.wbg.__wbg_getClientExtensionResults_37549795564cd9d3 = function() { return logError(function (arg0) {
var ret = getObject(arg0).getClientExtensionResults();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_instanceof_HtmlInputElement_8969541a2a0bded0 = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof HTMLInputElement;
_assertBoolean(ret);
return ret;
}, arguments) };
imports.wbg.__wbg_setchecked_f6ead3490df88a7f = function() { return logError(function (arg0, arg1) {
getObject(arg0).checked = arg1 !== 0; getObject(arg0).checked = arg1 !== 0;
}; }, arguments) };
imports.wbg.__wbg_value_fc1c354d1a0e9714 = function(arg0, arg1) { imports.wbg.__wbg_value_fc1c354d1a0e9714 = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).value; var ret = getObject(arg1).value;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN; var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0; getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbg_setvalue_ce4a23f487065c07 = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_get_5835a17331a9d8f2 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).get(getObject(arg1));
return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_instanceof_Event_39e54e1fe6593f4c = function(arg0) { imports.wbg.__wbg_setvalue_ce4a23f487065c07 = function() { return logError(function (arg0, arg1, arg2) {
var ret = getObject(arg0) instanceof Event; getObject(arg0).value = getStringFromWasm0(arg1, arg2);
return ret;
};
imports.wbg.__wbg_target_e560052e31e4567c = function(arg0) {
var ret = getObject(arg0).target;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
};
imports.wbg.__wbg_cancelBubble_17d7988ab2fbe4c9 = function(arg0) {
var ret = getObject(arg0).cancelBubble;
return ret;
};
imports.wbg.__wbg_preventDefault_fa00541ff125b78c = function(arg0) {
getObject(arg0).preventDefault();
};
imports.wbg.__wbg_instanceof_HtmlDocument_395ec6365cabde6c = function(arg0) {
var ret = getObject(arg0) instanceof HTMLDocument;
return ret;
};
imports.wbg.__wbg_cookie_66f4449cc764fcb2 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg1).cookie;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
}, arguments) }; }, arguments) };
imports.wbg.__wbg_addEventListener_55682f77717d7665 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { imports.wbg.__wbg_addEventListener_55682f77717d7665 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4)); getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
@ -555,64 +610,55 @@ async function init(input) {
imports.wbg.__wbg_removeEventListener_9cd36e5806463d5d = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { imports.wbg.__wbg_removeEventListener_9cd36e5806463d5d = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0); getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_credentials_403bf2de10e8f1c3 = function(arg0) { imports.wbg.__wbg_instanceof_HtmlDocument_395ec6365cabde6c = function() { return logError(function (arg0) {
var ret = getObject(arg0).credentials; var ret = getObject(arg0) instanceof HTMLDocument;
return addHeapObject(ret); _assertBoolean(ret);
};
imports.wbg.__wbg_instanceof_Element_c9423704dd5d9b1d = function(arg0) {
var ret = getObject(arg0) instanceof Element;
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_namespaceURI_e9a971e6c1ce68db = function(arg0, arg1) { imports.wbg.__wbg_cookie_66f4449cc764fcb2 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg1).cookie;
var ptr0 = 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_instanceof_Element_c9423704dd5d9b1d = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof Element;
_assertBoolean(ret);
return ret;
}, arguments) };
imports.wbg.__wbg_namespaceURI_e9a971e6c1ce68db = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg1).namespaceURI; var ret = getObject(arg1).namespaceURI;
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);
var len0 = WASM_VECTOR_LEN; var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0; getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0; getInt32Memory0()[arg0 / 4 + 0] = ptr0;
}; }, arguments) };
imports.wbg.__wbg_classList_5086913f676eb3f3 = function(arg0) { imports.wbg.__wbg_classList_5086913f676eb3f3 = function() { return logError(function (arg0) {
var ret = getObject(arg0).classList; var ret = getObject(arg0).classList;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_removeAttribute_1adaecf6b4d35a09 = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_removeAttribute_1adaecf6b4d35a09 = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).removeAttribute(getStringFromWasm0(arg1, arg2)); getObject(arg0).removeAttribute(getStringFromWasm0(arg1, arg2));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_setAttribute_1776fcc9b98d464e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { imports.wbg.__wbg_setAttribute_1776fcc9b98d464e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_instanceof_HtmlElement_d3e8f1c1d6788b24 = function(arg0) { imports.wbg.__wbg_instanceof_HtmlElement_d3e8f1c1d6788b24 = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof HTMLElement; var ret = getObject(arg0) instanceof HTMLElement;
_assertBoolean(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_focus_4434360545ac99cf = function() { return handleError(function (arg0) { imports.wbg.__wbg_focus_4434360545ac99cf = function() { return handleError(function (arg0) {
getObject(arg0).focus(); getObject(arg0).focus();
}, arguments) }; }, arguments) };
imports.wbg.__wbg_credentials_403bf2de10e8f1c3 = function() { return logError(function (arg0) {
var ret = getObject(arg0).credentials;
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_pushState_89ce908020e1d6aa = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) { imports.wbg.__wbg_pushState_89ce908020e1d6aa = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5)); getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_parentElement_96e1e07348340043 = function(arg0) {
var ret = getObject(arg0).parentElement;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
};
imports.wbg.__wbg_lastChild_e2b014abab089e08 = function(arg0) {
var ret = getObject(arg0).lastChild;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
};
imports.wbg.__wbg_setnodeValue_f175b74a390f8fda = function(arg0, arg1, arg2) {
getObject(arg0).nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_appendChild_3fe5090c665d3bb4 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).appendChild(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_insertBefore_4f09909023feac91 = function() { return handleError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).insertBefore(getObject(arg1), getObject(arg2));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_removeChild_f4a83c9698136bbb = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).removeChild(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_pathname_d0014089875ea691 = function() { return handleError(function (arg0, arg1) { imports.wbg.__wbg_pathname_d0014089875ea691 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg1).pathname; var ret = getObject(arg1).pathname;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -630,6 +676,39 @@ async function init(input) {
imports.wbg.__wbg_replace_ec236a3e3182c4da = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_replace_ec236a3e3182c4da = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).replace(getStringFromWasm0(arg1, arg2)); getObject(arg0).replace(getStringFromWasm0(arg1, arg2));
}, arguments) }; }, arguments) };
imports.wbg.__wbg_parentElement_96e1e07348340043 = function() { return logError(function (arg0) {
var ret = getObject(arg0).parentElement;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_lastChild_e2b014abab089e08 = function() { return logError(function (arg0) {
var ret = getObject(arg0).lastChild;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_setnodeValue_f175b74a390f8fda = function() { return logError(function (arg0, arg1, arg2) {
getObject(arg0).nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2);
}, arguments) };
imports.wbg.__wbg_appendChild_3fe5090c665d3bb4 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).appendChild(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_insertBefore_4f09909023feac91 = function() { return handleError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).insertBefore(getObject(arg1), getObject(arg2));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_removeChild_f4a83c9698136bbb = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).removeChild(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_get_bee69a0c35eec41c = function() { return handleError(function (arg0, arg1, arg2, arg3) {
var 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_f9448486a94c9aef = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) };
imports.wbg.__wbg_getItem_f92ef607397e96b1 = function() { return handleError(function (arg0, arg1, arg2, arg3) { imports.wbg.__wbg_getItem_f92ef607397e96b1 = function() { return handleError(function (arg0, arg1, arg2, arg3) {
var ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3)); var 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);
@ -643,49 +722,20 @@ async function init(input) {
imports.wbg.__wbg_setItem_279b13e5ad0b82cb = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { imports.wbg.__wbg_setItem_279b13e5ad0b82cb = 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_get_bee69a0c35eec41c = function() { return handleError(function (arg0, arg1, arg2, arg3) { imports.wbg.__wbg_instanceof_Response_ea36d565358a42f7 = function() { return logError(function (arg0) {
var 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_f9448486a94c9aef = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) };
imports.wbg.__wbg_pathname_7affbcff36f35c0e = function(arg0, arg1) {
var ret = getObject(arg1).pathname;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbg_new_4473c9af1cac368b = function() { return handleError(function (arg0, arg1) {
var ret = new URL(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_value_d3a30bc2c7caf357 = function(arg0, arg1) {
var ret = getObject(arg1).value;
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len0;
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
};
imports.wbg.__wbg_setvalue_6a34bab301f38bf2 = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_instanceof_Response_ea36d565358a42f7 = function(arg0) {
var ret = getObject(arg0) instanceof Response; var ret = getObject(arg0) instanceof Response;
_assertBoolean(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_status_3a55bb50e744b834 = function(arg0) { imports.wbg.__wbg_status_3a55bb50e744b834 = function() { return logError(function (arg0) {
var ret = getObject(arg0).status; var ret = getObject(arg0).status;
_assertNum(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_headers_e4204c6775f7b3b4 = function(arg0) { imports.wbg.__wbg_headers_e4204c6775f7b3b4 = function() { return logError(function (arg0) {
var ret = getObject(arg0).headers; var ret = getObject(arg0).headers;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_json_4ab99130d1a5b3a9 = function() { return handleError(function (arg0) { imports.wbg.__wbg_json_4ab99130d1a5b3a9 = function() { return handleError(function (arg0) {
var ret = getObject(arg0).json(); var ret = getObject(arg0).json();
return addHeapObject(ret); return addHeapObject(ret);
@ -694,66 +744,69 @@ async function init(input) {
var ret = getObject(arg0).text(); var ret = getObject(arg0).text();
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_new_16f24b0728c5e67b = function() { imports.wbg.__wbg_new_16f24b0728c5e67b = function() { return logError(function () {
var ret = new Array(); var ret = new Array();
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_newnoargs_f579424187aa1717 = function(arg0, arg1) { imports.wbg.__wbg_push_a72df856079e6930 = function() { return logError(function (arg0, arg1) {
var ret = new Function(getStringFromWasm0(arg0, arg1)); var ret = getObject(arg0).push(getObject(arg1));
_assertNum(ret);
return ret;
}, arguments) };
imports.wbg.__wbg_instanceof_Error_4287ce7d75f0e3a2 = function() { return logError(function (arg0) {
var ret = getObject(arg0) instanceof Error;
_assertBoolean(ret);
return ret;
}, arguments) };
imports.wbg.__wbg_message_1dfe93b595be8811 = function() { return logError(function (arg0) {
var ret = getObject(arg0).message;
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_get_8bbb82393651dd9c = function() { return handleError(function (arg0, arg1) { imports.wbg.__wbg_name_66305ab387468967 = function() { return logError(function (arg0) {
var ret = Reflect.get(getObject(arg0), getObject(arg1)); var ret = getObject(arg0).name;
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_toString_3e854a6a919f2996 = function() { return logError(function (arg0) {
var ret = getObject(arg0).toString();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_newnoargs_f579424187aa1717 = function() { return logError(function (arg0, arg1) {
var ret = new Function(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_call_89558c3e96703ca1 = function() { return handleError(function (arg0, arg1) { imports.wbg.__wbg_call_89558c3e96703ca1 = function() { return handleError(function (arg0, arg1) {
var ret = getObject(arg0).call(getObject(arg1)); var ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_new_d3138911a89329b0 = function() { imports.wbg.__wbg_valueOf_39e0d6bc7e4232b9 = function() { return logError(function (arg0) {
var ret = new Object();
return addHeapObject(ret);
};
imports.wbg.__wbg_push_a72df856079e6930 = function(arg0, arg1) {
var ret = getObject(arg0).push(getObject(arg1));
return ret;
};
imports.wbg.__wbg_instanceof_Error_4287ce7d75f0e3a2 = function(arg0) {
var ret = getObject(arg0) instanceof Error;
return ret;
};
imports.wbg.__wbg_message_1dfe93b595be8811 = function(arg0) {
var ret = getObject(arg0).message;
return addHeapObject(ret);
};
imports.wbg.__wbg_name_66305ab387468967 = function(arg0) {
var ret = getObject(arg0).name;
return addHeapObject(ret);
};
imports.wbg.__wbg_toString_3e854a6a919f2996 = function(arg0) {
var ret = getObject(arg0).toString();
return addHeapObject(ret);
};
imports.wbg.__wbg_valueOf_39e0d6bc7e4232b9 = function(arg0) {
var ret = getObject(arg0).valueOf(); var ret = getObject(arg0).valueOf();
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_is_3d73f4d91adacc37 = function(arg0, arg1) { imports.wbg.__wbg_is_3d73f4d91adacc37 = function() { return logError(function (arg0, arg1) {
var ret = Object.is(getObject(arg0), getObject(arg1)); var ret = Object.is(getObject(arg0), getObject(arg1));
_assertBoolean(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_resolve_4f8f547f26b30b27 = function(arg0) { imports.wbg.__wbg_new_d3138911a89329b0 = function() { return logError(function () {
var ret = new Object();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_resolve_4f8f547f26b30b27 = function() { return logError(function (arg0) {
var ret = Promise.resolve(getObject(arg0)); var ret = Promise.resolve(getObject(arg0));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_then_a6860c82b90816ca = function(arg0, arg1) { imports.wbg.__wbg_then_a6860c82b90816ca = function() { return logError(function (arg0, arg1) {
var ret = getObject(arg0).then(getObject(arg1)); var ret = getObject(arg0).then(getObject(arg1));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_then_58a04e42527f52c6 = function(arg0, arg1, arg2) { imports.wbg.__wbg_then_58a04e42527f52c6 = function() { return logError(function (arg0, arg1, arg2) {
var ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); var ret = getObject(arg0).then(getObject(arg1), getObject(arg2));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_globalThis_d61b1f48a57191ae = function() { return handleError(function () {
var ret = globalThis.globalThis;
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_self_e23d74ae45fb17d1 = function() { return handleError(function () { imports.wbg.__wbg_self_e23d74ae45fb17d1 = function() { return handleError(function () {
var ret = self.self; var ret = self.self;
return addHeapObject(ret); return addHeapObject(ret);
@ -762,35 +815,37 @@ async function init(input) {
var ret = window.window; var ret = window.window;
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_globalThis_d61b1f48a57191ae = function() { return handleError(function () {
var ret = globalThis.globalThis;
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_global_e7669da72fd7f239 = function() { return handleError(function () { imports.wbg.__wbg_global_e7669da72fd7f239 = function() { return handleError(function () {
var ret = global.global; var ret = global.global;
return addHeapObject(ret); return addHeapObject(ret);
}, arguments) }; }, arguments) };
imports.wbg.__wbg_buffer_5e74a88a1424a2e0 = function(arg0) { imports.wbg.__wbg_new_e3b800e570795b3c = function() { return logError(function (arg0) {
var ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_newwithbyteoffsetandlength_278ec7532799393a = function(arg0, arg1, arg2) {
var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
return addHeapObject(ret);
};
imports.wbg.__wbg_new_e3b800e570795b3c = function(arg0) {
var ret = new Uint8Array(getObject(arg0)); var ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbg_set_5b8081e9d002f0df = function(arg0, arg1, arg2) { imports.wbg.__wbg_newwithbyteoffsetandlength_278ec7532799393a = function() { return logError(function (arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0); var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
}; return addHeapObject(ret);
imports.wbg.__wbg_length_30803400a8f15c59 = function(arg0) { }, arguments) };
imports.wbg.__wbg_length_30803400a8f15c59 = function() { return logError(function (arg0) {
var ret = getObject(arg0).length; var ret = getObject(arg0).length;
_assertNum(ret);
return ret; return ret;
}; }, arguments) };
imports.wbg.__wbg_set_5b8081e9d002f0df = function() { return logError(function (arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
}, arguments) };
imports.wbg.__wbg_buffer_5e74a88a1424a2e0 = function() { return logError(function (arg0) {
var ret = getObject(arg0).buffer;
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_get_8bbb82393651dd9c = function() { return handleError(function (arg0, arg1) {
var ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_set_c42875065132a932 = function() { return handleError(function (arg0, arg1, arg2) { imports.wbg.__wbg_set_c42875065132a932 = function() { return handleError(function (arg0, arg1, arg2) {
var ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); var ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
_assertBoolean(ret);
return ret; return ret;
}, arguments) }; }, arguments) };
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
@ -807,18 +862,18 @@ async function init(input) {
var ret = wasm.memory; var ret = wasm.memory;
return addHeapObject(ret); return addHeapObject(ret);
}; };
imports.wbg.__wbindgen_closure_wrapper1345 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper12230 = function() { return logError(function (arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 563, __wbg_adapter_30); var ret = makeClosure(arg0, arg1, 856, __wbg_adapter_30);
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbindgen_closure_wrapper1575 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper15845 = function() { return logError(function (arg0, arg1, arg2) {
var ret = makeClosure(arg0, arg1, 609, __wbg_adapter_33); var ret = makeMutClosure(arg0, arg1, 884, __wbg_adapter_33);
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
imports.wbg.__wbindgen_closure_wrapper1700 = function(arg0, arg1, arg2) { imports.wbg.__wbindgen_closure_wrapper16308 = function() { return logError(function (arg0, arg1, arg2) {
var ret = makeMutClosure(arg0, arg1, 648, __wbg_adapter_36); var ret = makeMutClosure(arg0, arg1, 913, __wbg_adapter_36);
return addHeapObject(ret); return addHeapObject(ret);
}; }, arguments) };
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input); input = fetch(input);

View file

@ -0,0 +1,13 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export function run_app(a: number): void;
export function __wbindgen_malloc(a: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number): number;
export const __wbindgen_export_2: WebAssembly.Table;
export function _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h79af8eb94e22cb43(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h1c17030d49aa8a7b(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb210817f7291cc2b(a: number, b: number, c: number): void;
export function __wbindgen_add_to_stack_pointer(a: number): number;
export function __wbindgen_free(a: number, b: number): void;
export function __wbindgen_exn_store(a: number): void;

View file

@ -14,9 +14,11 @@
"files": [ "files": [
"kanidmd_web_ui_bg.wasm", "kanidmd_web_ui_bg.wasm",
"kanidmd_web_ui.js", "kanidmd_web_ui.js",
"kanidmd_web_ui.d.ts",
"LICENSE.md" "LICENSE.md"
], ],
"module": "kanidmd_web_ui.js", "module": "kanidmd_web_ui.js",
"homepage": "https://github.com/kanidm/kanidm/", "homepage": "https://github.com/kanidm/kanidm/",
"types": "kanidmd_web_ui.d.ts",
"sideEffects": false "sideEffects": false
} }