mirror of
https://github.com/kanidm/kanidm.git
synced 2025-06-03 06:43:55 +02:00
At some point, you have to pay for your tech debt. (#759)
This replaces the unmaintained serde_cbor with serde_json in both db and IPC contexts. It changes the database on disk format to align better to how we structure values in memory making it faster to load entries when they aren't cached. And this breaks down the horrible ValueSet enum to dyn trait types, which has a huge performance improvement to the server.
This commit is contained in:
parent
deedb9560b
commit
241e0eeb4d
Cargo.lock
kanidm_unix_int
kanidmd
daemon
idm
Cargo.toml
src
score/src/https
orca
56
Cargo.lock
generated
56
Cargo.lock
generated
|
@ -964,6 +964,7 @@ dependencies = [
|
|||
"score",
|
||||
"serde",
|
||||
"structopt",
|
||||
"tikv-jemallocator",
|
||||
"tokio",
|
||||
"toml",
|
||||
"users",
|
||||
|
@ -1122,6 +1123,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
|
@ -1919,27 +1926,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "jemalloc-sys"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"fs_extra",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jemallocator"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69"
|
||||
dependencies = [
|
||||
"jemalloc-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "joinery"
|
||||
version = "2.1.0"
|
||||
|
@ -1967,14 +1953,13 @@ dependencies = [
|
|||
"compiled-uuid",
|
||||
"concread",
|
||||
"criterion",
|
||||
"either",
|
||||
"dyn-clone",
|
||||
"fernet",
|
||||
"filetime",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"hashbrown 0.12.1",
|
||||
"idlset",
|
||||
"jemallocator",
|
||||
"kanidm_proto",
|
||||
"lazy_static",
|
||||
"ldap3_proto",
|
||||
|
@ -2086,7 +2071,7 @@ dependencies = [
|
|||
"rusqlite",
|
||||
"score",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -2549,7 +2534,6 @@ dependencies = [
|
|||
"csv",
|
||||
"dialoguer",
|
||||
"futures-util",
|
||||
"jemallocator",
|
||||
"kanidm_client",
|
||||
"kanidm_proto",
|
||||
"ldap3_proto",
|
||||
|
@ -2560,6 +2544,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"tikv-jemallocator",
|
||||
"tokio",
|
||||
"tokio-openssl",
|
||||
"tokio-util",
|
||||
|
@ -3770,6 +3755,27 @@ dependencies = [
|
|||
"tide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tikv-jemalloc-sys"
|
||||
version = "0.4.3+5.2.1-patched.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1792ccb507d955b46af42c123ea8863668fae24d03721e40cad6a41773dbb49"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"fs_extra",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tikv-jemallocator"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5b7bcecfafe4998587d636f9ae9d55eb9d0499877b88757767c346875067098"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"tikv-jemalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
|
|
|
@ -60,7 +60,7 @@ bytes = "^1.1.0"
|
|||
|
||||
libc = "^0.2.126"
|
||||
serde = { version = "^1.0.137", features = ["derive"] }
|
||||
serde_cbor = "^0.11.2"
|
||||
serde_json = "^1.0.80"
|
||||
structopt = { version = "0.3.26", default-features = false }
|
||||
|
||||
libsqlite3-sys = "0.24.2"
|
||||
|
|
|
@ -18,7 +18,7 @@ impl Decoder for ClientCodec {
|
|||
type Error = IoError;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_cbor::from_slice::<ClientResponse>(&src) {
|
||||
match serde_json::from_slice::<ClientResponse>(&src) {
|
||||
Ok(msg) => {
|
||||
// Clear the buffer for the next message.
|
||||
src.clear();
|
||||
|
@ -33,9 +33,9 @@ impl Encoder<ClientRequest> for ClientCodec {
|
|||
type Error = IoError;
|
||||
|
||||
fn encode(&mut self, msg: ClientRequest, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
let data = serde_cbor::to_vec(&msg).map_err(|e| {
|
||||
let data = serde_json::to_vec(&msg).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
IoError::new(ErrorKind::Other, "CBOR encode error")
|
||||
IoError::new(ErrorKind::Other, "JSON encode error")
|
||||
})?;
|
||||
debug!("Attempting to send request -> {:?} ...", data);
|
||||
dst.put(data.as_slice());
|
||||
|
|
|
@ -31,9 +31,9 @@ pub fn call_daemon_blocking(
|
|||
})
|
||||
.map_err(Box::new)?;
|
||||
|
||||
let data = serde_cbor::to_vec(&req).map_err(|e| {
|
||||
let data = serde_json::to_vec(&req).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
Box::new(IoError::new(ErrorKind::Other, "CBOR encode error"))
|
||||
Box::new(IoError::new(ErrorKind::Other, "JSON encode error"))
|
||||
})?;
|
||||
// .map_err(Box::new)?;
|
||||
|
||||
|
@ -101,9 +101,9 @@ pub fn call_daemon_blocking(
|
|||
data.truncate(counter);
|
||||
|
||||
// Now attempt to decode.
|
||||
let cr = serde_cbor::from_slice::<ClientResponse>(data.as_slice()).map_err(|e| {
|
||||
let cr = serde_json::from_slice::<ClientResponse>(data.as_slice()).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
Box::new(IoError::new(ErrorKind::Other, "CBOR decode error"))
|
||||
Box::new(IoError::new(ErrorKind::Other, "JSON decode error"))
|
||||
})?;
|
||||
|
||||
Ok(cr)
|
||||
|
|
|
@ -55,7 +55,7 @@ impl Decoder for ClientCodec {
|
|||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_cbor::from_slice::<ClientRequest>(&src) {
|
||||
match serde_json::from_slice::<ClientRequest>(&src) {
|
||||
Ok(msg) => {
|
||||
// Clear the buffer for the next message.
|
||||
src.clear();
|
||||
|
@ -71,9 +71,9 @@ impl Encoder<ClientResponse> for ClientCodec {
|
|||
|
||||
fn encode(&mut self, msg: ClientResponse, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
debug!("Attempting to send response -> {:?} ...", msg);
|
||||
let data = serde_cbor::to_vec(&msg).map_err(|e| {
|
||||
let data = serde_json::to_vec(&msg).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, "CBOR encode error")
|
||||
io::Error::new(io::ErrorKind::Other, "JSON encode error")
|
||||
})?;
|
||||
dst.put(data.as_slice());
|
||||
Ok(())
|
||||
|
@ -93,7 +93,7 @@ impl Decoder for TaskCodec {
|
|||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_cbor::from_slice::<TaskResponse>(&src) {
|
||||
match serde_json::from_slice::<TaskResponse>(&src) {
|
||||
Ok(msg) => {
|
||||
// Clear the buffer for the next message.
|
||||
src.clear();
|
||||
|
@ -109,9 +109,9 @@ impl Encoder<TaskRequest> for TaskCodec {
|
|||
|
||||
fn encode(&mut self, msg: TaskRequest, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
debug!("Attempting to send request -> {:?} ...", msg);
|
||||
let data = serde_cbor::to_vec(&msg).map_err(|e| {
|
||||
let data = serde_json::to_vec(&msg).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, "CBOR encode error")
|
||||
io::Error::new(io::ErrorKind::Other, "JSON encode error")
|
||||
})?;
|
||||
dst.put(data.as_slice());
|
||||
Ok(())
|
||||
|
|
|
@ -260,9 +260,9 @@ impl<'a> DbTxn<'a> {
|
|||
let r: Result<Option<(_, _)>, ()> = data
|
||||
.first()
|
||||
.map(|(token, expiry)| {
|
||||
// token convert with cbor.
|
||||
let t = serde_cbor::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
// token convert with json.
|
||||
let t = serde_json::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
let e = u64::try_from(*expiry).map_err(|e| {
|
||||
error!("u64 convert error -> {:?}", e);
|
||||
|
@ -296,17 +296,17 @@ impl<'a> DbTxn<'a> {
|
|||
|
||||
data.iter()
|
||||
.map(|token| {
|
||||
// token convert with cbor.
|
||||
serde_cbor::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
// token convert with json.
|
||||
serde_json::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn update_account(&self, account: &UnixUserToken, expire: u64) -> Result<(), ()> {
|
||||
let data = serde_cbor::to_vec(account).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
let data = serde_json::to_vec(account).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
let expire = i64::try_from(expire).map_err(|e| {
|
||||
error!("i64 convert error -> {:?}", e);
|
||||
|
@ -423,8 +423,8 @@ impl<'a> DbTxn<'a> {
|
|||
error!("password error -> {:?}", e);
|
||||
})?;
|
||||
let dbpw = pw.to_dbpasswordv1();
|
||||
let data = serde_cbor::to_vec(&dbpw).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
let data = serde_json::to_vec(&dbpw).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
|
||||
self.conn
|
||||
|
@ -479,8 +479,8 @@ impl<'a> DbTxn<'a> {
|
|||
.first()
|
||||
.map(|raw| {
|
||||
// Map the option from data.first.
|
||||
let dbpw: DbPasswordV1 = serde_cbor::from_slice(raw.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
let dbpw: DbPasswordV1 = serde_json::from_slice(raw.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
let pw = Password::try_from(dbpw)?;
|
||||
pw.verify(cred).map_err(|e| {
|
||||
|
@ -555,9 +555,9 @@ impl<'a> DbTxn<'a> {
|
|||
let r: Result<Option<(_, _)>, ()> = data
|
||||
.first()
|
||||
.map(|(token, expiry)| {
|
||||
// token convert with cbor.
|
||||
let t = serde_cbor::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
// token convert with json.
|
||||
let t = serde_json::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
let e = u64::try_from(*expiry).map_err(|e| {
|
||||
error!("u64 convert error -> {:?}", e);
|
||||
|
@ -593,10 +593,10 @@ impl<'a> DbTxn<'a> {
|
|||
|
||||
data.iter()
|
||||
.map(|token| {
|
||||
// token convert with cbor.
|
||||
// token convert with json.
|
||||
// debug!("{:?}", token);
|
||||
serde_cbor::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
serde_json::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
|
@ -625,18 +625,18 @@ impl<'a> DbTxn<'a> {
|
|||
|
||||
data.iter()
|
||||
.map(|token| {
|
||||
// token convert with cbor.
|
||||
// token convert with json.
|
||||
// debug!("{:?}", token);
|
||||
serde_cbor::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
serde_json::from_slice(token.as_slice()).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn update_group(&self, grp: &UnixGroupToken, expire: u64) -> Result<(), ()> {
|
||||
let data = serde_cbor::to_vec(grp).map_err(|e| {
|
||||
error!("cbor error -> {:?}", e);
|
||||
let data = serde_json::to_vec(grp).map_err(|e| {
|
||||
error!("json error -> {:?}", e);
|
||||
})?;
|
||||
let expire = i64::try_from(expire).map_err(|e| {
|
||||
error!("i64 convert error -> {:?}", e);
|
||||
|
|
|
@ -42,7 +42,7 @@ impl Decoder for TaskCodec {
|
|||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match serde_cbor::from_slice::<TaskRequest>(&src) {
|
||||
match serde_json::from_slice::<TaskRequest>(&src) {
|
||||
Ok(msg) => {
|
||||
// Clear the buffer for the next message.
|
||||
src.clear();
|
||||
|
@ -58,9 +58,9 @@ impl Encoder<TaskResponse> for TaskCodec {
|
|||
|
||||
fn encode(&mut self, msg: TaskResponse, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
debug!("Attempting to send request -> {:?} ...", msg);
|
||||
let data = serde_cbor::to_vec(&msg).map_err(|e| {
|
||||
let data = serde_json::to_vec(&msg).map_err(|e| {
|
||||
error!("socket encoding error -> {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, "CBOR encode error")
|
||||
io::Error::new(io::ErrorKind::Other, "JSON encode error")
|
||||
})?;
|
||||
dst.put(data.as_slice());
|
||||
Ok(())
|
||||
|
|
|
@ -22,6 +22,7 @@ score = { path = "../score" }
|
|||
structopt = { version = "^0.3.26", default-features = false }
|
||||
users = "^0.11.0"
|
||||
serde = { version = "^1.0.137", features = ["derive"] }
|
||||
tikv-jemallocator = "0.4.0"
|
||||
tokio = { version = "^1.18.0", features = ["rt-multi-thread", "macros", "signal"] }
|
||||
toml = "0.5.9"
|
||||
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
#[cfg(all(jemallocator, not(test)))]
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
use users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@ tracing = { version = "^0.1.34", features = ["attributes"] }
|
|||
tracing-subscriber = { version = "^0.3.11", features = ["env-filter"] }
|
||||
tracing-serde = "^0.1.3"
|
||||
|
||||
jemallocator = { version = "0.3.2", optional = true }
|
||||
either = "^1.6.1"
|
||||
dyn-clone = "^1.0.5"
|
||||
|
||||
url = { version = "^2.2.2", features = ["serde"] }
|
||||
tide = "^0.16.0"
|
||||
|
|
|
@ -74,7 +74,7 @@ impl AccessControlSearch {
|
|||
}
|
||||
|
||||
let attrs = value
|
||||
.get_ava_as_str("acp_search_attr")
|
||||
.get_ava_iter_iutf8("acp_search_attr")
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing acp_search_attr");
|
||||
OperationError::InvalidAcpState("Missing acp_search_attr".to_string())
|
||||
|
@ -170,12 +170,12 @@ impl AccessControlCreate {
|
|||
}
|
||||
|
||||
let attrs = value
|
||||
.get_ava_as_str("acp_create_attr")
|
||||
.get_ava_iter_iutf8("acp_create_attr")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let classes = value
|
||||
.get_ava_as_str("acp_create_class")
|
||||
.get_ava_iter_iutf8("acp_create_class")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -229,17 +229,17 @@ impl AccessControlModify {
|
|||
}
|
||||
|
||||
let presattrs = value
|
||||
.get_ava_as_str("acp_modify_presentattr")
|
||||
.get_ava_iter_iutf8("acp_modify_presentattr")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let remattrs = value
|
||||
.get_ava_as_str("acp_modify_removedattr")
|
||||
.get_ava_iter_iutf8("acp_modify_removedattr")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let classes = value
|
||||
.get_ava_as_str("acp_modify_class")
|
||||
.get_ava_iter_iutf8("acp_modify_class")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -310,14 +310,14 @@ impl AccessControlProfile {
|
|||
|
||||
// copy name
|
||||
let name = value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing name");
|
||||
OperationError::InvalidAcpState("Missing name".to_string())
|
||||
})?
|
||||
.to_string();
|
||||
// copy uuid
|
||||
let uuid = *value.get_uuid();
|
||||
let uuid = value.get_uuid();
|
||||
// receiver, and turn to real filter
|
||||
let receiver_f: ProtoFilter = value
|
||||
.get_ava_single_protofilter("acp_receiver")
|
||||
|
@ -998,7 +998,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
// I still think if this is None, we should just fail here ...
|
||||
// because it shouldn't be possible to match.
|
||||
|
||||
let create_classes: BTreeSet<&str> = match e.get_ava_as_str("class") {
|
||||
let create_classes: BTreeSet<&str> = match e.get_ava_iter_iutf8("class") {
|
||||
Some(s) => s.collect(),
|
||||
None => {
|
||||
admin_error!("Class set failed to build - corrupted entry?");
|
||||
|
@ -1269,7 +1269,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
.collect();
|
||||
|
||||
AccessEffectivePermission {
|
||||
target: *e.get_uuid(),
|
||||
target: e.get_uuid(),
|
||||
search: search_effective,
|
||||
modify_pres,
|
||||
modify_rem,
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
use crate::be::dbvalue::DbValueV1;
|
||||
use crate::be::dbvalue::{DbValueEmailAddressV1, DbValuePhoneNumberV1, DbValueSetV2, DbValueV1};
|
||||
use crate::prelude::OperationError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DbEntryV1 {
|
||||
pub attrs: BTreeMap<AttrString, Vec<DbValueV1>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DbEntryV2 {
|
||||
pub attrs: BTreeMap<AttrString, DbValueSetV2>,
|
||||
}
|
||||
|
||||
// REMEMBER: If you add a new version here, you MUST
|
||||
// update entry.rs into_dbentry to export to the latest
|
||||
// update entry.rs to_dbentry to export to the latest
|
||||
// type always!!
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum DbEntryVers {
|
||||
V1(DbEntryV1),
|
||||
V2(DbEntryV2),
|
||||
}
|
||||
|
||||
// This is actually what we store into the DB.
|
||||
|
@ -22,6 +31,392 @@ pub struct DbEntry {
|
|||
pub ent: DbEntryVers,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DbBackup {
|
||||
V1(Vec<DbEntry>),
|
||||
V2 {
|
||||
db_s_uuid: Uuid,
|
||||
db_d_uuid: Uuid,
|
||||
db_ts_max: Duration,
|
||||
entries: Vec<DbEntry>,
|
||||
},
|
||||
}
|
||||
|
||||
fn from_vec_dbval1(attr_val: Vec<DbValueV1>) -> Result<DbValueSetV2, OperationError> {
|
||||
// ========================
|
||||
//
|
||||
let mut viter = attr_val.into_iter().peekable();
|
||||
|
||||
match viter.peek() {
|
||||
Some(DbValueV1::Utf8(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Utf8(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Utf8(vs))
|
||||
}
|
||||
Some(DbValueV1::Iutf8(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Iutf8(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Iutf8(vs))
|
||||
}
|
||||
Some(DbValueV1::Iname(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Iname(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Iname(vs))
|
||||
}
|
||||
Some(DbValueV1::Uuid(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Uuid(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Uuid(vs))
|
||||
}
|
||||
Some(DbValueV1::Bool(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Bool(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Bool(vs))
|
||||
}
|
||||
Some(DbValueV1::SyntaxType(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::SyntaxType(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::SyntaxType(vs))
|
||||
}
|
||||
Some(DbValueV1::IndexType(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::IndexType(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::IndexType(vs))
|
||||
}
|
||||
Some(DbValueV1::Reference(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Reference(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Reference(vs))
|
||||
}
|
||||
Some(DbValueV1::JsonFilter(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::JsonFilter(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::JsonFilter(vs))
|
||||
}
|
||||
Some(DbValueV1::Credential(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Credential(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Credential(vs))
|
||||
}
|
||||
Some(DbValueV1::SecretValue(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::SecretValue(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::SecretValue(vs))
|
||||
}
|
||||
Some(DbValueV1::SshKey(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::SshKey(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::SshKey(vs))
|
||||
}
|
||||
Some(DbValueV1::Spn(_, _)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Spn(n, d) = dbv {
|
||||
Ok((n, d))
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Spn(vs))
|
||||
}
|
||||
Some(DbValueV1::Uint32(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Uint32(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Uint32(vs))
|
||||
}
|
||||
Some(DbValueV1::Cid(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Cid(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Cid(vs))
|
||||
}
|
||||
Some(DbValueV1::NsUniqueId(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::NsUniqueId(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::NsUniqueId(vs))
|
||||
}
|
||||
Some(DbValueV1::DateTime(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::DateTime(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::DateTime(vs))
|
||||
}
|
||||
Some(DbValueV1::EmailAddress(_)) => {
|
||||
let mut primary = None;
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::EmailAddress(DbValueEmailAddressV1 { d, p }) = dbv {
|
||||
if p {
|
||||
primary = Some(d.clone());
|
||||
}
|
||||
Ok(d)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let primary = primary.ok_or(OperationError::InvalidValueState)?;
|
||||
vs.map(|vs| DbValueSetV2::EmailAddress(primary, vs))
|
||||
}
|
||||
Some(DbValueV1::PhoneNumber(_)) => {
|
||||
let mut primary = None;
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::PhoneNumber(DbValuePhoneNumberV1 { d, p }) = dbv {
|
||||
if p {
|
||||
primary = Some(d.clone());
|
||||
}
|
||||
Ok(d)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let primary = primary.ok_or(OperationError::InvalidValueState)?;
|
||||
vs.map(|vs| DbValueSetV2::PhoneNumber(primary, vs))
|
||||
}
|
||||
Some(DbValueV1::Address(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Address(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Address(vs))
|
||||
}
|
||||
Some(DbValueV1::Url(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::Url(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::Url(vs))
|
||||
}
|
||||
Some(DbValueV1::OauthScope(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::OauthScope(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::OauthScope(vs))
|
||||
}
|
||||
Some(DbValueV1::OauthScopeMap(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::OauthScopeMap(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::OauthScopeMap(vs))
|
||||
}
|
||||
Some(DbValueV1::PrivateBinary(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::PrivateBinary(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::PrivateBinary(vs))
|
||||
}
|
||||
Some(DbValueV1::PublicBinary(_, _)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::PublicBinary(t, s) = dbv {
|
||||
Ok((t, s))
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::PublicBinary(vs))
|
||||
}
|
||||
Some(DbValueV1::RestrictedString(_)) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::RestrictedString(s) = dbv {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::RestrictedString(vs))
|
||||
}
|
||||
Some(DbValueV1::IntentToken { u: _, s: _ }) => {
|
||||
let vs: Result<Vec<_>, _> = viter
|
||||
.map(|dbv| {
|
||||
if let DbValueV1::IntentToken { u, s } = dbv {
|
||||
Ok((u, s))
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
vs.map(|vs| DbValueSetV2::IntentToken(vs))
|
||||
}
|
||||
// Neither of these should exist yet.
|
||||
Some(DbValueV1::TrustedDeviceEnrollment { u: _ })
|
||||
| Some(DbValueV1::AuthSession { u: _ })
|
||||
| None => {
|
||||
// Shiiiiii
|
||||
assert!(false);
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DbEntry {
|
||||
pub(crate) fn to_v2(self) -> Result<Self, OperationError> {
|
||||
if let DbEntryVers::V1(dbe) = self.ent {
|
||||
dbe.attrs
|
||||
.into_iter()
|
||||
.filter(|(_attr_name, attr_val)| {
|
||||
// Skip anything that is empty, because our from impl
|
||||
// can't handle it, neither can our dbvaluesetv2
|
||||
!attr_val.is_empty()
|
||||
})
|
||||
.map(|(attr_name, attr_val)| {
|
||||
from_vec_dbval1(attr_val).map(|attr_val_2| (attr_name, attr_val_2))
|
||||
})
|
||||
.collect::<Result<BTreeMap<_, _>, _>>()
|
||||
.map(|attrs| DbEntry {
|
||||
ent: DbEntryVers::V2(DbEntryV2 { attrs }),
|
||||
})
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DbEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match &self.ent {
|
||||
|
@ -36,6 +431,15 @@ impl std::fmt::Debug for DbEntry {
|
|||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
DbEntryVers::V2(dbe_v2) => {
|
||||
write!(f, "v2 - {{ ")?;
|
||||
for (k, vs) in dbe_v2.attrs.iter() {
|
||||
write!(f, "{} - [", k)?;
|
||||
write!(f, "{:?}, ", vs)?;
|
||||
write!(f, "], ")?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +474,25 @@ impl std::fmt::Display for DbEntry {
|
|||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
DbEntryVers::V2(dbe_v2) => {
|
||||
write!(f, "v2 - {{ ")?;
|
||||
match dbe_v2.attrs.get("uuid") {
|
||||
Some(uuids) => {
|
||||
write!(f, "{:?}, ", uuids)?;
|
||||
}
|
||||
None => write!(f, "Uuid(INVALID), ")?,
|
||||
};
|
||||
if let Some(names) = dbe_v2.attrs.get("name") {
|
||||
write!(f, "{:?}, ", names)?;
|
||||
}
|
||||
if let Some(names) = dbe_v2.attrs.get("attributename") {
|
||||
write!(f, "{:?}, ", names)?;
|
||||
}
|
||||
if let Some(names) = dbe_v2.attrs.get("classname") {
|
||||
write!(f, "{:?}, ", names)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,6 +252,108 @@ pub enum DbValueV1 {
|
|||
AuthSession { u: Uuid },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum DbValueSetV2 {
|
||||
#[serde(rename = "U8")]
|
||||
Utf8(Vec<String>),
|
||||
#[serde(rename = "I8")]
|
||||
Iutf8(Vec<String>),
|
||||
#[serde(rename = "N8")]
|
||||
Iname(Vec<String>),
|
||||
#[serde(rename = "UU")]
|
||||
Uuid(Vec<Uuid>),
|
||||
#[serde(rename = "BO")]
|
||||
Bool(Vec<bool>),
|
||||
#[serde(rename = "SY")]
|
||||
SyntaxType(Vec<usize>),
|
||||
#[serde(rename = "IN")]
|
||||
IndexType(Vec<usize>),
|
||||
#[serde(rename = "RF")]
|
||||
Reference(Vec<Uuid>),
|
||||
#[serde(rename = "JF")]
|
||||
JsonFilter(Vec<String>),
|
||||
#[serde(rename = "CR")]
|
||||
Credential(Vec<DbValueCredV1>),
|
||||
#[serde(rename = "RU")]
|
||||
SecretValue(Vec<String>),
|
||||
#[serde(rename = "SK")]
|
||||
SshKey(Vec<DbValueTaggedStringV1>),
|
||||
#[serde(rename = "SP")]
|
||||
Spn(Vec<(String, String)>),
|
||||
#[serde(rename = "UI")]
|
||||
Uint32(Vec<u32>),
|
||||
#[serde(rename = "CI")]
|
||||
Cid(Vec<DbCidV1>),
|
||||
#[serde(rename = "NU")]
|
||||
NsUniqueId(Vec<String>),
|
||||
#[serde(rename = "DT")]
|
||||
DateTime(Vec<String>),
|
||||
#[serde(rename = "EM")]
|
||||
EmailAddress(String, Vec<String>),
|
||||
#[serde(rename = "PN")]
|
||||
PhoneNumber(String, Vec<String>),
|
||||
#[serde(rename = "AD")]
|
||||
Address(Vec<DbValueAddressV1>),
|
||||
#[serde(rename = "UR")]
|
||||
Url(Vec<Url>),
|
||||
#[serde(rename = "OS")]
|
||||
OauthScope(Vec<String>),
|
||||
#[serde(rename = "OM")]
|
||||
OauthScopeMap(Vec<DbValueOauthScopeMapV1>),
|
||||
#[serde(rename = "E2")]
|
||||
PrivateBinary(Vec<Vec<u8>>),
|
||||
#[serde(rename = "PB")]
|
||||
PublicBinary(Vec<(String, Vec<u8>)>),
|
||||
#[serde(rename = "RS")]
|
||||
RestrictedString(Vec<String>),
|
||||
#[serde(rename = "IT")]
|
||||
IntentToken(Vec<(Uuid, DbValueIntentTokenStateV1)>),
|
||||
#[serde(rename = "TE")]
|
||||
TrustedDeviceEnrollment(Vec<Uuid>),
|
||||
#[serde(rename = "AS")]
|
||||
AuthSession(Vec<Uuid>),
|
||||
}
|
||||
|
||||
impl DbValueSetV2 {
|
||||
pub fn len(&self) -> usize {
|
||||
match &self {
|
||||
DbValueSetV2::Utf8(set) => set.len(),
|
||||
DbValueSetV2::Iutf8(set) => set.len(),
|
||||
DbValueSetV2::Iname(set) => set.len(),
|
||||
DbValueSetV2::Uuid(set) => set.len(),
|
||||
DbValueSetV2::Bool(set) => set.len(),
|
||||
DbValueSetV2::SyntaxType(set) => set.len(),
|
||||
DbValueSetV2::IndexType(set) => set.len(),
|
||||
DbValueSetV2::Reference(set) => set.len(),
|
||||
DbValueSetV2::JsonFilter(set) => set.len(),
|
||||
DbValueSetV2::Credential(set) => set.len(),
|
||||
DbValueSetV2::SecretValue(set) => set.len(),
|
||||
DbValueSetV2::SshKey(set) => set.len(),
|
||||
DbValueSetV2::Spn(set) => set.len(),
|
||||
DbValueSetV2::Uint32(set) => set.len(),
|
||||
DbValueSetV2::Cid(set) => set.len(),
|
||||
DbValueSetV2::NsUniqueId(set) => set.len(),
|
||||
DbValueSetV2::DateTime(set) => set.len(),
|
||||
DbValueSetV2::EmailAddress(_primary, set) => set.len(),
|
||||
DbValueSetV2::PhoneNumber(_primary, set) => set.len(),
|
||||
DbValueSetV2::Address(set) => set.len(),
|
||||
DbValueSetV2::Url(set) => set.len(),
|
||||
DbValueSetV2::OauthScope(set) => set.len(),
|
||||
DbValueSetV2::OauthScopeMap(set) => set.len(),
|
||||
DbValueSetV2::PrivateBinary(set) => set.len(),
|
||||
DbValueSetV2::PublicBinary(set) => set.len(),
|
||||
DbValueSetV2::RestrictedString(set) => set.len(),
|
||||
DbValueSetV2::IntentToken(set) => set.len(),
|
||||
DbValueSetV2::TrustedDeviceEnrollment(set) => set.len(),
|
||||
DbValueSetV2::AuthSession(set) => set.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::be::dbvalue::DbCredV1;
|
||||
|
|
|
@ -219,6 +219,7 @@ macro_rules! name2uuid {
|
|||
let cache_key = NameCacheKey::Name2Uuid($name.to_string());
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::U(uuid)) = cache_r {
|
||||
trace!(?uuid, "Got cached name2uuid");
|
||||
return Ok(Some(uuid.clone()));
|
||||
} else {
|
||||
trace!("Cache miss uuid for name2uuid");
|
||||
|
@ -241,9 +242,10 @@ macro_rules! uuid2spn {
|
|||
$uuid:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::uuid2spn", {
|
||||
let cache_key = NameCacheKey::Uuid2Spn(*$uuid);
|
||||
let cache_key = NameCacheKey::Uuid2Spn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::S(ref spn)) = cache_r {
|
||||
trace!(?spn, "Got cached uuid2spn");
|
||||
return Ok(Some(spn.as_ref().clone()));
|
||||
} else {
|
||||
trace!("Cache miss spn for uuid2spn");
|
||||
|
@ -266,7 +268,7 @@ macro_rules! uuid2rdn {
|
|||
$uuid:expr
|
||||
) => {{
|
||||
spanned!("be::idl_arc_sqlite::uuid2rdn", {
|
||||
let cache_key = NameCacheKey::Uuid2Rdn(*$uuid);
|
||||
let cache_key = NameCacheKey::Uuid2Rdn($uuid);
|
||||
let cache_r = $self.name_cache.get(&cache_key);
|
||||
if let Some(NameCacheValue::R(ref rdn)) = cache_r {
|
||||
return Ok(Some(rdn.clone()));
|
||||
|
@ -336,15 +338,17 @@ pub trait IdlArcSqliteTransaction {
|
|||
|
||||
fn get_db_d_uuid(&self) -> Result<Option<Uuid>, OperationError>;
|
||||
|
||||
fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError>;
|
||||
|
||||
fn verify(&self) -> Vec<Result<(), ConsistencyError>>;
|
||||
|
||||
fn is_dirty(&self) -> bool;
|
||||
|
||||
fn name2uuid(&mut self, name: &str) -> Result<Option<Uuid>, OperationError>;
|
||||
|
||||
fn uuid2spn(&mut self, uuid: &Uuid) -> Result<Option<Value>, OperationError>;
|
||||
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError>;
|
||||
|
||||
fn uuid2rdn(&mut self, uuid: &Uuid) -> Result<Option<String>, OperationError>;
|
||||
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError>;
|
||||
|
||||
fn list_idxs(&self) -> Result<Vec<String>, OperationError>;
|
||||
|
||||
|
@ -391,6 +395,10 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteReadTransaction<'a> {
|
|||
self.db.get_db_d_uuid()
|
||||
}
|
||||
|
||||
fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
|
||||
self.db.get_db_ts_max()
|
||||
}
|
||||
|
||||
fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
|
||||
verify!(self)
|
||||
}
|
||||
|
@ -403,11 +411,11 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteReadTransaction<'a> {
|
|||
name2uuid!(self, name)
|
||||
}
|
||||
|
||||
fn uuid2spn(&mut self, uuid: &Uuid) -> Result<Option<Value>, OperationError> {
|
||||
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
uuid2spn!(self, uuid)
|
||||
}
|
||||
|
||||
fn uuid2rdn(&mut self, uuid: &Uuid) -> Result<Option<String>, OperationError> {
|
||||
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
|
||||
uuid2rdn!(self, uuid)
|
||||
}
|
||||
|
||||
|
@ -468,6 +476,13 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteWriteTransaction<'a> {
|
|||
self.db.get_db_d_uuid()
|
||||
}
|
||||
|
||||
fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
|
||||
match *self.op_ts_max {
|
||||
Some(ts) => Ok(Some(ts)),
|
||||
None => self.db.get_db_ts_max(),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
|
||||
verify!(self)
|
||||
}
|
||||
|
@ -480,11 +495,11 @@ impl<'a> IdlArcSqliteTransaction for IdlArcSqliteWriteTransaction<'a> {
|
|||
name2uuid!(self, name)
|
||||
}
|
||||
|
||||
fn uuid2spn(&mut self, uuid: &Uuid) -> Result<Option<Value>, OperationError> {
|
||||
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
uuid2spn!(self, uuid)
|
||||
}
|
||||
|
||||
fn uuid2rdn(&mut self, uuid: &Uuid) -> Result<Option<String>, OperationError> {
|
||||
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
|
||||
uuid2rdn!(self, uuid)
|
||||
}
|
||||
|
||||
|
@ -566,17 +581,17 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
.iter_mut_mark_clean()
|
||||
.try_for_each(|(k, v)| match (k, v) {
|
||||
(NameCacheKey::Name2Uuid(k), Some(NameCacheValue::U(v))) => {
|
||||
db.write_name2uuid_add(k, v)
|
||||
db.write_name2uuid_add(k, *v)
|
||||
}
|
||||
(NameCacheKey::Name2Uuid(k), None) => db.write_name2uuid_rem(k),
|
||||
(NameCacheKey::Uuid2Spn(uuid), Some(NameCacheValue::S(v))) => {
|
||||
db.write_uuid2spn(uuid, Some(v))
|
||||
db.write_uuid2spn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(uuid, None),
|
||||
(NameCacheKey::Uuid2Spn(uuid), None) => db.write_uuid2spn(*uuid, None),
|
||||
(NameCacheKey::Uuid2Rdn(uuid), Some(NameCacheValue::R(v))) => {
|
||||
db.write_uuid2rdn(uuid, Some(v))
|
||||
db.write_uuid2rdn(*uuid, Some(v))
|
||||
}
|
||||
(NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(uuid, None),
|
||||
(NameCacheKey::Uuid2Rdn(uuid), None) => db.write_uuid2rdn(*uuid, None),
|
||||
|
||||
_ => Err(OperationError::InvalidCacheState),
|
||||
})
|
||||
|
@ -953,13 +968,13 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
|
||||
pub fn write_name2uuid_add(
|
||||
&mut self,
|
||||
uuid: &Uuid,
|
||||
uuid: Uuid,
|
||||
add: BTreeSet<String>,
|
||||
) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_name2uuid_add", {
|
||||
add.into_iter().for_each(|k| {
|
||||
let cache_key = NameCacheKey::Name2Uuid(k);
|
||||
let cache_value = NameCacheValue::U(*uuid);
|
||||
let cache_value = NameCacheValue::U(uuid);
|
||||
self.name_cache.insert_dirty(cache_key, cache_value)
|
||||
});
|
||||
Ok(())
|
||||
|
@ -983,9 +998,9 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
self.db.create_uuid2spn()
|
||||
}
|
||||
|
||||
pub fn write_uuid2spn(&mut self, uuid: &Uuid, k: Option<Value>) -> Result<(), OperationError> {
|
||||
pub fn write_uuid2spn(&mut self, uuid: Uuid, k: Option<Value>) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_uuid2spn", {
|
||||
let cache_key = NameCacheKey::Uuid2Spn(*uuid);
|
||||
let cache_key = NameCacheKey::Uuid2Spn(uuid);
|
||||
match k {
|
||||
Some(v) => self
|
||||
.name_cache
|
||||
|
@ -1000,9 +1015,9 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
self.db.create_uuid2rdn()
|
||||
}
|
||||
|
||||
pub fn write_uuid2rdn(&mut self, uuid: &Uuid, k: Option<String>) -> Result<(), OperationError> {
|
||||
pub fn write_uuid2rdn(&mut self, uuid: Uuid, k: Option<String>) -> Result<(), OperationError> {
|
||||
spanned!("be::idl_arc_sqlite::write_uuid2rdn", {
|
||||
let cache_key = NameCacheKey::Uuid2Rdn(*uuid);
|
||||
let cache_key = NameCacheKey::Uuid2Rdn(uuid);
|
||||
match k {
|
||||
Some(s) => self
|
||||
.name_cache
|
||||
|
@ -1041,18 +1056,11 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
self.db.write_db_d_uuid(nsid)
|
||||
}
|
||||
|
||||
pub fn set_db_ts_max(&mut self, ts: &Duration) -> Result<(), OperationError> {
|
||||
*self.op_ts_max = Some(*ts);
|
||||
pub fn set_db_ts_max(&mut self, ts: Duration) -> Result<(), OperationError> {
|
||||
*self.op_ts_max = Some(ts);
|
||||
self.db.set_db_ts_max(ts)
|
||||
}
|
||||
|
||||
pub fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
|
||||
match *self.op_ts_max {
|
||||
Some(ts) => Ok(Some(ts)),
|
||||
None => self.db.get_db_ts_max(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_db_index_version(&self) -> i64 {
|
||||
self.db.get_db_index_version()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use crate::be::dbentry::DbEntry;
|
||||
use crate::be::dbvalue::DbValueSetV2;
|
||||
use crate::be::{BackendConfig, IdList, IdRawEntry, IdxKey, IdxSlope};
|
||||
use crate::entry::{Entry, EntryCommitted, EntrySealed};
|
||||
use crate::prelude::*;
|
||||
use crate::value::{IndexType, Value};
|
||||
use crate::valueset;
|
||||
use hashbrown::HashMap;
|
||||
use idlset::v2::IDLBitRange;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
@ -28,9 +31,9 @@ fn sqlite_error(e: rusqlite::Error) -> OperationError {
|
|||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)] // needs to accept value from `map_err`
|
||||
fn serde_cbor_error(e: serde_cbor::Error) -> OperationError {
|
||||
admin_error!(?e, "Serde CBOR Error");
|
||||
OperationError::SerdeCborError
|
||||
fn serde_json_error(e: serde_json::Error) -> OperationError {
|
||||
admin_error!(?e, "Serde JSON Error");
|
||||
OperationError::SerdeJsonError
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
|
@ -240,7 +243,7 @@ pub trait IdlSqliteTransaction {
|
|||
.map_err(sqlite_error)?;
|
||||
|
||||
let idl = match idl_raw {
|
||||
Some(d) => serde_cbor::from_slice(d.as_slice()).map_err(serde_cbor_error)?,
|
||||
Some(d) => serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?,
|
||||
// We don't have this value, it must be empty (or we
|
||||
// have a corrupted index .....
|
||||
None => IDLBitRange::new(),
|
||||
|
@ -271,7 +274,7 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
fn uuid2spn(&mut self, uuid: &Uuid) -> Result<Option<Value>, OperationError> {
|
||||
fn uuid2spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
spanned!("be::idl_sqlite::uuid2spn", {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
|
@ -287,9 +290,10 @@ pub trait IdlSqliteTransaction {
|
|||
|
||||
let spn: Option<Value> = match spn_raw {
|
||||
Some(d) => {
|
||||
let dbv = serde_cbor::from_slice(d.as_slice()).map_err(serde_cbor_error)?;
|
||||
let dbv: DbValueSetV2 =
|
||||
serde_json::from_slice(d.as_slice()).map_err(serde_json_error)?;
|
||||
|
||||
ValueSet::from_db_valuev1_iter(std::iter::once(dbv))
|
||||
valueset::from_db_valueset_v2(dbv)
|
||||
.map_err(|_| OperationError::CorruptedIndex("uuid2spn".to_string()))
|
||||
.map(|vs| vs.to_value_single())?
|
||||
}
|
||||
|
@ -302,7 +306,7 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
fn uuid2rdn(&mut self, uuid: &Uuid) -> Result<Option<String>, OperationError> {
|
||||
fn uuid2rdn(&mut self, uuid: Uuid) -> Result<Option<String>, OperationError> {
|
||||
spanned!("be::idl_sqlite::uuid2rdn", {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
// The table exists - lets now get the actual index itself.
|
||||
|
@ -340,11 +344,15 @@ pub trait IdlSqliteTransaction {
|
|||
.map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(serde_cbor::from_slice(d.as_slice()).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
})?),
|
||||
Some(d) => Some(
|
||||
serde_json::from_slice(d.as_slice())
|
||||
.or_else(|e| serde_cbor::from_slice(d.as_slice()).map_err(|_| e))
|
||||
.map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
})?,
|
||||
),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
@ -367,11 +375,48 @@ pub trait IdlSqliteTransaction {
|
|||
.map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(serde_cbor::from_slice(d.as_slice()).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
})?),
|
||||
Some(d) => Some(
|
||||
serde_json::from_slice(d.as_slice())
|
||||
.or_else(|e| serde_cbor::from_slice(d.as_slice()).map_err(|_| e))
|
||||
.map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
})?,
|
||||
),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
|
||||
// Try to get a value.
|
||||
let data: Option<Vec<u8>> = self
|
||||
.get_conn()
|
||||
.query_row("SELECT data FROM db_op_ts WHERE id = 1", [], |row| {
|
||||
row.get(0)
|
||||
})
|
||||
.optional()
|
||||
// this whole `map` call is useless
|
||||
.map(|e_opt| {
|
||||
// If we have a row, we try to make it a sid
|
||||
e_opt.map(|e| {
|
||||
let y: Vec<u8> = e;
|
||||
y
|
||||
})
|
||||
// If no sid, we return none.
|
||||
})
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(
|
||||
serde_json::from_slice(d.as_slice())
|
||||
.or_else(|_| serde_cbor::from_slice(d.as_slice()))
|
||||
.map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde JSON Error");
|
||||
eprintln!("CRITICAL: Serde JSON Error -> {:?}", e);
|
||||
OperationError::SerdeJsonError
|
||||
})?,
|
||||
),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
@ -454,8 +499,8 @@ pub trait IdlSqliteTransaction {
|
|||
idx_iter
|
||||
.map(|v| {
|
||||
v.map_err(sqlite_error).and_then(|KeyIdl { key, data }| {
|
||||
serde_cbor::from_slice(data.as_slice())
|
||||
.map_err(serde_cbor_error)
|
||||
serde_json::from_slice(data.as_slice())
|
||||
.map_err(serde_json_error)
|
||||
.map(|idl| (key, idl))
|
||||
})
|
||||
})
|
||||
|
@ -626,7 +671,7 @@ impl IdlSqliteWriteTransaction {
|
|||
entry: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<(), OperationError> {
|
||||
let dbe = entry.to_dbentry();
|
||||
let data = serde_cbor::to_vec(&dbe).map_err(serde_cbor_error)?;
|
||||
let data = serde_json::to_vec(&dbe).map_err(serde_json_error)?;
|
||||
|
||||
let raw_entries = std::iter::once(IdRawEntry {
|
||||
id: entry.get_id(),
|
||||
|
@ -742,7 +787,7 @@ impl IdlSqliteWriteTransaction {
|
|||
} else {
|
||||
trace!(?idl, "writing idl");
|
||||
// Serialise the IdList to Vec<u8>
|
||||
let idl_raw = serde_cbor::to_vec(idl).map_err(serde_cbor_error)?;
|
||||
let idl_raw = serde_json::to_vec(idl).map_err(serde_json_error)?;
|
||||
|
||||
// update or create it.
|
||||
let query = format!(
|
||||
|
@ -776,7 +821,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map_err(sqlite_error)
|
||||
}
|
||||
|
||||
pub fn write_name2uuid_add(&self, name: &str, uuid: &Uuid) -> Result<(), OperationError> {
|
||||
pub fn write_name2uuid_add(&self, name: &str, uuid: Uuid) -> Result<(), OperationError> {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
|
||||
self.conn
|
||||
|
@ -809,12 +854,37 @@ impl IdlSqliteWriteTransaction {
|
|||
.map_err(sqlite_error)
|
||||
}
|
||||
|
||||
pub fn write_uuid2spn(&self, uuid: &Uuid, k: Option<&Value>) -> Result<(), OperationError> {
|
||||
pub fn migrate_dbentryv1_to_dbentryv2(&self) -> Result<(), OperationError> {
|
||||
let allids = self.get_identry_raw(&IdList::AllIds)?;
|
||||
let raw_entries: Result<Vec<IdRawEntry>, _> = allids
|
||||
.into_iter()
|
||||
.map(|raw| {
|
||||
serde_cbor::from_slice(raw.data.as_slice())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Serde CBOR Error");
|
||||
OperationError::SerdeCborError
|
||||
})
|
||||
.and_then(|dbe: DbEntry| dbe.to_v2())
|
||||
.and_then(|dbe| {
|
||||
serde_json::to_vec(&dbe)
|
||||
.map(|data| IdRawEntry { id: raw.id, data })
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Serde Json Error");
|
||||
OperationError::SerdeJsonError
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.write_identries_raw(raw_entries?.into_iter())
|
||||
}
|
||||
|
||||
pub fn write_uuid2spn(&self, uuid: Uuid, k: Option<&Value>) -> Result<(), OperationError> {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
match k {
|
||||
Some(k) => {
|
||||
let dbv1 = k.to_supplementary_db_valuev1();
|
||||
let data = serde_cbor::to_vec(&dbv1).map_err(serde_cbor_error)?;
|
||||
let data = serde_json::to_vec(&dbv1).map_err(serde_json_error)?;
|
||||
self.conn
|
||||
.prepare("INSERT OR REPLACE INTO idx_uuid2spn (uuid, spn) VALUES(:uuid, :spn)")
|
||||
.and_then(|mut stmt| {
|
||||
|
@ -845,7 +915,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map_err(sqlite_error)
|
||||
}
|
||||
|
||||
pub fn write_uuid2rdn(&self, uuid: &Uuid, k: Option<&String>) -> Result<(), OperationError> {
|
||||
pub fn write_uuid2rdn(&self, uuid: Uuid, k: Option<&String>) -> Result<(), OperationError> {
|
||||
let uuids = uuid.as_hyphenated().to_string();
|
||||
match k {
|
||||
Some(k) => self
|
||||
|
@ -971,10 +1041,10 @@ impl IdlSqliteWriteTransaction {
|
|||
}
|
||||
|
||||
pub fn write_db_s_uuid(&self, nsid: Uuid) -> Result<(), OperationError> {
|
||||
let data = serde_cbor::to_vec(&nsid).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
let data = serde_json::to_vec(&nsid).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde JSON Error");
|
||||
eprintln!("CRITICAL: Serde JSON Error -> {:?}", e);
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
||||
self.conn
|
||||
|
@ -994,10 +1064,10 @@ impl IdlSqliteWriteTransaction {
|
|||
}
|
||||
|
||||
pub fn write_db_d_uuid(&self, nsid: Uuid) -> Result<(), OperationError> {
|
||||
let data = serde_cbor::to_vec(&nsid).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
let data = serde_json::to_vec(&nsid).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde JSON Error");
|
||||
eprintln!("CRITICAL: Serde JSON Error -> {:?}", e);
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
||||
self.conn
|
||||
|
@ -1016,11 +1086,11 @@ impl IdlSqliteWriteTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn set_db_ts_max(&self, ts: &Duration) -> Result<(), OperationError> {
|
||||
let data = serde_cbor::to_vec(ts).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
pub fn set_db_ts_max(&self, ts: Duration) -> Result<(), OperationError> {
|
||||
let data = serde_json::to_vec(&ts).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde JSON Error");
|
||||
eprintln!("CRITICAL: Serde JSON Error -> {:?}", e);
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
||||
self.conn
|
||||
|
@ -1039,35 +1109,6 @@ impl IdlSqliteWriteTransaction {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_db_ts_max(&self) -> Result<Option<Duration>, OperationError> {
|
||||
// Try to get a value.
|
||||
let data: Option<Vec<u8>> = self
|
||||
.get_conn()
|
||||
.query_row("SELECT data FROM db_op_ts WHERE id = 1", [], |row| {
|
||||
row.get(0)
|
||||
})
|
||||
.optional()
|
||||
// this whole `map` call is useless
|
||||
.map(|e_opt| {
|
||||
// If we have a row, we try to make it a sid
|
||||
e_opt.map(|e| {
|
||||
let y: Vec<u8> = e;
|
||||
y
|
||||
})
|
||||
// If no sid, we return none.
|
||||
})
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(serde_cbor::from_slice(d.as_slice()).map_err(|e| {
|
||||
admin_error!(immediate = true, ?e, "CRITICAL: Serde CBOR Error");
|
||||
eprintln!("CRITICAL: Serde CBOR Error -> {:?}", e);
|
||||
OperationError::SerdeCborError
|
||||
})?),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
// ===== inner helpers =====
|
||||
// Some of these are not self due to use in new()
|
||||
fn get_db_version_key(&self, key: &str) -> i64 {
|
||||
|
@ -1202,7 +1243,14 @@ impl IdlSqliteWriteTransaction {
|
|||
dbv_id2entry = 4;
|
||||
admin_info!(entry = %dbv_id2entry, "dbv_id2entry migrated (name2uuid, uuid2spn, uuid2rdn)");
|
||||
}
|
||||
// * if v4 -> complete.
|
||||
// * if v4 -> migrate v1 to v2 entries.
|
||||
if dbv_id2entry == 4 {
|
||||
self.migrate_dbentryv1_to_dbentryv2()?;
|
||||
dbv_id2entry = 5;
|
||||
admin_info!(entry = %dbv_id2entry, "dbv_id2entry migrated (dbentryv1 -> dbentryv2)");
|
||||
}
|
||||
|
||||
// * if v5 -> complete.
|
||||
|
||||
self.set_db_version_key(DBV_ID2ENTRY, dbv_id2entry)
|
||||
.map_err(sqlite_error)?;
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::cell::UnsafeCell;
|
|||
use std::sync::Arc;
|
||||
use tracing::{trace, trace_span};
|
||||
|
||||
use crate::be::dbentry::DbEntry;
|
||||
use crate::be::dbentry::{DbBackup, DbEntry};
|
||||
use crate::entry::{Entry, EntryCommitted, EntryNew, EntrySealed};
|
||||
use crate::filter::{Filter, FilterPlan, FilterResolved, FilterValidResolved};
|
||||
use crate::identity::Limits;
|
||||
|
@ -129,18 +129,18 @@ pub struct BackendWriteTransaction<'a> {
|
|||
|
||||
impl IdRawEntry {
|
||||
fn into_dbentry(self) -> Result<(u64, DbEntry), OperationError> {
|
||||
serde_cbor::from_slice(self.data.as_slice())
|
||||
serde_json::from_slice(self.data.as_slice())
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "Serde CBOR Error");
|
||||
OperationError::SerdeCborError
|
||||
admin_error!(?e, "Serde JSON Error");
|
||||
OperationError::SerdeJsonError
|
||||
})
|
||||
.map(|dbe| (self.id, dbe))
|
||||
}
|
||||
|
||||
fn into_entry(self) -> Result<Entry<EntrySealed, EntryCommitted>, OperationError> {
|
||||
let db_e = serde_cbor::from_slice(self.data.as_slice()).map_err(|e| {
|
||||
admin_error!(?e, "Serde CBOR Error");
|
||||
OperationError::SerdeCborError
|
||||
let db_e = serde_json::from_slice(self.data.as_slice()).map_err(|e| {
|
||||
admin_error!(?e, "Serde JSON Error");
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
// let id = u64::try_from(self.id).map_err(|_| OperationError::InvalidEntryId)?;
|
||||
Entry::from_dbentry(db_e, self.id).ok_or(OperationError::CorruptedEntry(self.id))
|
||||
|
@ -718,7 +718,7 @@ pub trait BackendTransaction {
|
|||
.iter()
|
||||
.try_for_each(|name| match self.get_idlayer().name2uuid(name) {
|
||||
Ok(Some(idx_uuid)) => {
|
||||
if &idx_uuid == e_uuid {
|
||||
if idx_uuid == e_uuid {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!("Invalid name2uuid state -> incorrect uuid association");
|
||||
|
@ -792,19 +792,37 @@ pub trait BackendTransaction {
|
|||
// load all entries into RAM, may need to change this later
|
||||
// if the size of the database compared to RAM is an issue
|
||||
let idl = IdList::AllIds;
|
||||
let raw_entries: Vec<IdRawEntry> = self.get_idlayer().get_identry_raw(&idl)?;
|
||||
let idlayer = self.get_idlayer();
|
||||
let raw_entries: Vec<IdRawEntry> = idlayer.get_identry_raw(&idl)?;
|
||||
|
||||
let entries: Result<Vec<DbEntry>, _> = raw_entries
|
||||
.iter()
|
||||
.map(|id_ent| {
|
||||
serde_cbor::from_slice(id_ent.data.as_slice())
|
||||
serde_json::from_slice(id_ent.data.as_slice())
|
||||
.map_err(|_| OperationError::SerdeJsonError) // log?
|
||||
})
|
||||
.collect();
|
||||
|
||||
let entries = entries?;
|
||||
|
||||
let serialized_entries_str = serde_json::to_string_pretty(&entries).map_err(|e| {
|
||||
let db_s_uuid = idlayer
|
||||
.get_db_s_uuid()
|
||||
.and_then(|u| u.ok_or(OperationError::InvalidDbState))?;
|
||||
let db_d_uuid = idlayer
|
||||
.get_db_d_uuid()
|
||||
.and_then(|u| u.ok_or(OperationError::InvalidDbState))?;
|
||||
let db_ts_max = idlayer
|
||||
.get_db_ts_max()
|
||||
.and_then(|u| u.ok_or(OperationError::InvalidDbState))?;
|
||||
|
||||
let bak = DbBackup::V2 {
|
||||
db_s_uuid,
|
||||
db_d_uuid,
|
||||
db_ts_max,
|
||||
entries,
|
||||
};
|
||||
|
||||
let serialized_entries_str = serde_json::to_string(&bak).map_err(|e| {
|
||||
admin_error!(?e, "serde error");
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
@ -821,11 +839,11 @@ pub trait BackendTransaction {
|
|||
self.get_idlayer().name2uuid(name)
|
||||
}
|
||||
|
||||
fn uuid2spn(&self, uuid: &Uuid) -> Result<Option<Value>, OperationError> {
|
||||
fn uuid2spn(&self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
self.get_idlayer().uuid2spn(uuid)
|
||||
}
|
||||
|
||||
fn uuid2rdn(&self, uuid: &Uuid) -> Result<Option<String>, OperationError> {
|
||||
fn uuid2rdn(&self, uuid: Uuid) -> Result<Option<String>, OperationError> {
|
||||
self.get_idlayer().uuid2rdn(uuid)
|
||||
}
|
||||
}
|
||||
|
@ -1340,27 +1358,53 @@ impl<'a> BackendWriteTransaction<'a> {
|
|||
e
|
||||
})?;
|
||||
|
||||
let dbentries_option: Result<Vec<DbEntry>, serde_json::Error> =
|
||||
let dbbak_option: Result<DbBackup, serde_json::Error> =
|
||||
serde_json::from_str(&serialized_string);
|
||||
|
||||
let dbentries = dbentries_option.map_err(|e| {
|
||||
let dbbak = dbbak_option.map_err(|e| {
|
||||
admin_error!("serde_json error {:?}", e);
|
||||
OperationError::SerdeJsonError
|
||||
})?;
|
||||
|
||||
let dbentries = match dbbak {
|
||||
DbBackup::V1(dbentries) => dbentries,
|
||||
DbBackup::V2 {
|
||||
db_s_uuid,
|
||||
db_d_uuid,
|
||||
db_ts_max,
|
||||
entries,
|
||||
} => {
|
||||
// Do stuff.
|
||||
idlayer.write_db_s_uuid(db_s_uuid)?;
|
||||
idlayer.write_db_d_uuid(db_d_uuid)?;
|
||||
idlayer.set_db_ts_max(db_ts_max)?;
|
||||
entries
|
||||
}
|
||||
};
|
||||
|
||||
info!("Restoring {} entries ...", dbentries.len());
|
||||
|
||||
// Migrate any v1 entries to v2 if needed.
|
||||
let dbentries = dbentries
|
||||
.into_iter()
|
||||
.map(|dbe| dbe.to_v2())
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Now, we setup all the entries with new ids.
|
||||
let mut id_max = 0;
|
||||
let identries: Result<Vec<IdRawEntry>, _> = dbentries
|
||||
.iter()
|
||||
.map(|e| {
|
||||
id_max += 1;
|
||||
let data = serde_cbor::to_vec(&e).map_err(|_| OperationError::SerdeCborError)?;
|
||||
let data = serde_json::to_vec(&e).map_err(|_| OperationError::SerdeCborError)?;
|
||||
Ok(IdRawEntry { id: id_max, data })
|
||||
})
|
||||
.collect();
|
||||
|
||||
idlayer.write_identries_raw(identries?.into_iter())?;
|
||||
|
||||
info!("Restored {} entries", dbentries.len());
|
||||
|
||||
// Reindex now we are loaded.
|
||||
self.reindex()?;
|
||||
|
||||
|
@ -1424,15 +1468,15 @@ impl<'a> BackendWriteTransaction<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_db_ts_max(&self, ts: &Duration) -> Result<(), OperationError> {
|
||||
pub fn set_db_ts_max(&self, ts: Duration) -> Result<(), OperationError> {
|
||||
self.get_idlayer().set_db_ts_max(ts)
|
||||
}
|
||||
|
||||
pub fn get_db_ts_max(&self, ts: &Duration) -> Result<Duration, OperationError> {
|
||||
pub fn get_db_ts_max(&self, ts: Duration) -> Result<Duration, OperationError> {
|
||||
// if none, return ts. If found, return it.
|
||||
match self.get_idlayer().get_db_ts_max()? {
|
||||
Some(dts) => Ok(dts),
|
||||
None => Ok(*ts),
|
||||
None => Ok(ts),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1578,10 +1622,11 @@ mod tests {
|
|||
use super::{
|
||||
Backend, BackendConfig, BackendTransaction, BackendWriteTransaction, IdList, OperationError,
|
||||
};
|
||||
use super::{DbEntry, IdxKey};
|
||||
use super::{DbBackup, IdxKey};
|
||||
use crate::identity::Limits;
|
||||
use crate::prelude::*;
|
||||
use crate::value::{IndexType, PartialValue, Value};
|
||||
use std::time::Duration;
|
||||
|
||||
macro_rules! run_test {
|
||||
($test_fn:expr) => {{
|
||||
|
@ -1875,6 +1920,11 @@ mod tests {
|
|||
);
|
||||
eprintln!(" ⚠️ {}", db_backup_file_name);
|
||||
run_test!(|be: &mut BackendWriteTransaction| {
|
||||
// Important! Need db metadata setup!
|
||||
be.reset_db_s_uuid().unwrap();
|
||||
be.reset_db_d_uuid().unwrap();
|
||||
be.set_db_ts_max(Duration::from_secs(1)).unwrap();
|
||||
|
||||
// First create some entries (3?)
|
||||
let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
|
||||
e1.add_ava("userid", Value::from("william"));
|
||||
|
@ -1926,6 +1976,10 @@ mod tests {
|
|||
);
|
||||
eprintln!(" ⚠️ {}", db_backup_file_name);
|
||||
run_test!(|be: &mut BackendWriteTransaction| {
|
||||
// Important! Need db metadata setup!
|
||||
be.reset_db_s_uuid().unwrap();
|
||||
be.reset_db_d_uuid().unwrap();
|
||||
be.set_db_ts_max(Duration::from_secs(1)).unwrap();
|
||||
// First create some entries (3?)
|
||||
let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
|
||||
e1.add_ava("userid", Value::from("william"));
|
||||
|
@ -1966,10 +2020,24 @@ mod tests {
|
|||
|
||||
// Now here, we need to tamper with the file.
|
||||
let serialized_string = fs::read_to_string(&db_backup_file_name).unwrap();
|
||||
let mut dbentries: Vec<DbEntry> = serde_json::from_str(&serialized_string).unwrap();
|
||||
let _ = dbentries.pop();
|
||||
let mut dbbak: DbBackup = serde_json::from_str(&serialized_string).unwrap();
|
||||
|
||||
let serialized_entries_str = serde_json::to_string_pretty(&dbentries).unwrap();
|
||||
match &mut dbbak {
|
||||
DbBackup::V1(_) => {
|
||||
// We no longer use these format versions!
|
||||
unreachable!()
|
||||
}
|
||||
DbBackup::V2 {
|
||||
db_s_uuid: _,
|
||||
db_d_uuid: _,
|
||||
db_ts_max: _,
|
||||
entries,
|
||||
} => {
|
||||
let _ = entries.pop();
|
||||
}
|
||||
};
|
||||
|
||||
let serialized_entries_str = serde_json::to_string_pretty(&dbbak).unwrap();
|
||||
fs::write(&db_backup_file_name, serialized_entries_str).unwrap();
|
||||
|
||||
be.restore(&db_backup_file_name).expect("Restore failed!");
|
||||
|
@ -2090,11 +2158,11 @@ mod tests {
|
|||
assert!(be.name2uuid("william") == Ok(Some(william_uuid)));
|
||||
assert!(be.name2uuid("db237e8a-0079-4b8c-8a56-593b22aa44d1") == Ok(None));
|
||||
// check uuid2spn
|
||||
assert!(be.uuid2spn(&claire_uuid) == Ok(Some(Value::new_iname("claire"))));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(Some(Value::new_iname("william"))));
|
||||
assert!(be.uuid2spn(claire_uuid) == Ok(Some(Value::new_iname("claire"))));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(Some(Value::new_iname("william"))));
|
||||
// check uuid2rdn
|
||||
assert!(be.uuid2rdn(&claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(Some("name=william".to_string())));
|
||||
assert!(be.uuid2rdn(claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(Some("name=william".to_string())));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2129,8 +2197,8 @@ mod tests {
|
|||
|
||||
let william_uuid = Uuid::parse_str("db237e8a-0079-4b8c-8a56-593b22aa44d1").unwrap();
|
||||
assert!(be.name2uuid("william") == Ok(Some(william_uuid)));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(Some(Value::from("william"))));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(Some("name=william".to_string())));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(Some(Value::from("william"))));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(Some("name=william".to_string())));
|
||||
|
||||
// == Now we delete, and assert we removed the items.
|
||||
be.delete(&rset).unwrap();
|
||||
|
@ -2150,8 +2218,8 @@ mod tests {
|
|||
idl_state!(be, "uuid", IndexType::Presence, "_", Some(Vec::new()));
|
||||
|
||||
assert!(be.name2uuid("william") == Ok(None));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(None));
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2204,16 +2272,18 @@ mod tests {
|
|||
let lucy_uuid = Uuid::parse_str("7b23c99d-c06b-4a9a-a958-3afa56383e1d").unwrap();
|
||||
|
||||
assert!(be.name2uuid("claire") == Ok(Some(claire_uuid)));
|
||||
assert!(be.uuid2spn(&claire_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(&claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
let x = be.uuid2spn(claire_uuid);
|
||||
trace!(?x);
|
||||
assert!(be.uuid2spn(claire_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
|
||||
assert!(be.name2uuid("william") == Ok(None));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(None));
|
||||
|
||||
assert!(be.name2uuid("lucy") == Ok(None));
|
||||
assert!(be.uuid2spn(&lucy_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(&lucy_uuid) == Ok(None));
|
||||
assert!(be.uuid2spn(lucy_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(lucy_uuid) == Ok(None));
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2259,8 +2329,8 @@ mod tests {
|
|||
let william_uuid = Uuid::parse_str("db237e8a-0079-4b8c-8a56-593b22aa44d1").unwrap();
|
||||
assert!(be.name2uuid("william") == Ok(None));
|
||||
assert!(be.name2uuid("claire") == Ok(Some(william_uuid)));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(Some("name=claire".to_string())));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(Some("name=claire".to_string())));
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2314,10 +2384,10 @@ mod tests {
|
|||
let william_uuid = Uuid::parse_str("db237e8a-0079-4b8c-8a56-593b22aa44d1").unwrap();
|
||||
assert!(be.name2uuid("william") == Ok(None));
|
||||
assert!(be.name2uuid("claire") == Ok(Some(claire_uuid)));
|
||||
assert!(be.uuid2spn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(&william_uuid) == Ok(None));
|
||||
assert!(be.uuid2spn(&claire_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(&claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
assert!(be.uuid2spn(william_uuid) == Ok(None));
|
||||
assert!(be.uuid2rdn(william_uuid) == Ok(None));
|
||||
assert!(be.uuid2spn(claire_uuid) == Ok(Some(Value::from("claire"))));
|
||||
assert!(be.uuid2rdn(claire_uuid) == Ok(Some("name=claire".to_string())));
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub use crate::constants::system_config::*;
|
|||
pub use crate::constants::uuids::*;
|
||||
|
||||
// Increment this as we add new schema types and values!!!
|
||||
pub const SYSTEM_INDEX_VERSION: i64 = 20;
|
||||
pub const SYSTEM_INDEX_VERSION: i64 = 22;
|
||||
// On test builds, define to 60 seconds
|
||||
#[cfg(test)]
|
||||
pub const PURGE_FREQUENCY: u64 = 60;
|
||||
|
|
|
@ -374,9 +374,7 @@ pub const JSON_SCHEMA_ATTR_BADLIST_PASSWORD: &str = r#"{
|
|||
"description": [
|
||||
"A password that is badlisted meaning that it can not be set as a valid password by any user account."
|
||||
],
|
||||
"index": [
|
||||
"EQUALITY"
|
||||
],
|
||||
"index": [],
|
||||
"unique": [
|
||||
"false"
|
||||
],
|
||||
|
|
|
@ -33,18 +33,20 @@ use crate::repl::cid::Cid;
|
|||
use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
|
||||
use crate::value::{IndexType, SyntaxType};
|
||||
use crate::value::{IntentTokenState, PartialValue, Value};
|
||||
use crate::valueset::ValueSet;
|
||||
use crate::valueset::{self, ValueSet};
|
||||
use kanidm_proto::v1::Entry as ProtoEntry;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use kanidm_proto::v1::{OperationError, SchemaError};
|
||||
use tracing::trace;
|
||||
|
||||
use crate::be::dbentry::{DbEntry, DbEntryV1, DbEntryVers};
|
||||
use crate::be::dbentry::{DbEntry, DbEntryV2, DbEntryVers};
|
||||
use crate::be::dbvalue::DbValueSetV2;
|
||||
use crate::be::{IdxKey, IdxSlope};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
|
||||
use smartstring::alias::String as AttrString;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap as Map;
|
||||
pub use std::collections::BTreeSet as Set;
|
||||
use std::collections::BTreeSet;
|
||||
|
@ -165,7 +167,10 @@ fn compare_attrs(left: &Map<AttrString, ValueSet>, right: &Map<AttrString, Value
|
|||
|
||||
allkeys.into_iter().all(|k| {
|
||||
// Both must be Some, and both must have the same interiors.
|
||||
left.get(k) == right.get(k)
|
||||
match (left.get(k), right.get(k)) {
|
||||
(Some(l), Some(r)) => l.eq(r),
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -212,6 +217,7 @@ where
|
|||
f.debug_struct("Entry<EntrySealed, _>")
|
||||
.field("state", &self.state)
|
||||
.field("valid", &self.valid)
|
||||
.field("attrs", &self.attrs)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +236,7 @@ impl<STATE> std::fmt::Display for Entry<EntryInit, STATE> {
|
|||
|
||||
impl<STATE> Entry<EntryInit, STATE> {
|
||||
/// Get the uuid of this entry.
|
||||
pub(crate) fn get_uuid(&self) -> Option<&Uuid> {
|
||||
pub(crate) fn get_uuid(&self) -> Option<Uuid> {
|
||||
self.attrs.get("uuid").and_then(|vs| vs.to_uuid_single())
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +279,7 @@ impl Entry<EntryInit, EntryNew> {
|
|||
trace!(?k, ?v, "attribute");
|
||||
let nk = qs.get_schema().normalise_attr_name(k);
|
||||
let nv =
|
||||
ValueSet::from_result_value_iter(v.iter().map(|vr| qs.clone_value(&nk, vr)));
|
||||
valueset::from_result_value_iter(v.iter().map(|vr| qs.clone_value(&nk, vr)));
|
||||
trace!(?nv, "new valueset transform");
|
||||
match nv {
|
||||
Ok(nvi) => Ok((nk, nvi)),
|
||||
|
@ -332,115 +338,133 @@ impl Entry<EntryInit, EntryNew> {
|
|||
let attr = AttrString::from(k.to_lowercase());
|
||||
let vv: ValueSet = match attr.as_str() {
|
||||
"attributename" | "classname" | "domain" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_iutf8(&v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_iutf8(&v))
|
||||
)
|
||||
}
|
||||
"name" | "domain_name" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_iname(&v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_iname(&v))
|
||||
)
|
||||
}
|
||||
"userid" | "uidnumber" => {
|
||||
warn!("WARNING: Use of unstabilised attributes userid/uidnumber");
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_iutf8(&v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_iutf8(&v))
|
||||
)
|
||||
}
|
||||
"class" | "acp_create_class" | "acp_modify_class" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_class(v.as_str())).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_class(v.as_str()))
|
||||
)
|
||||
}
|
||||
"acp_create_attr" | "acp_search_attr" | "acp_modify_removedattr" | "acp_modify_presentattr" |
|
||||
"systemmay" | "may" | "systemmust" | "must"
|
||||
=> {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_attr(v.as_str())).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_attr(v.as_str()))
|
||||
)
|
||||
}
|
||||
"uuid" | "domain_uuid" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_uuids(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_uuids(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
"member" | "memberof" | "directmemberof" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_refer_s(v.as_str()).unwrap() ).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_refer_s(v.as_str()).unwrap() )
|
||||
)
|
||||
}
|
||||
"acp_enable" | "multivalue" | "unique" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_bools(v.as_str())
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_bools(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
).collect();
|
||||
vs.unwrap()
|
||||
)
|
||||
)
|
||||
}
|
||||
"syntax" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_syntaxs(v.as_str())
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_syntaxs(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
).collect();
|
||||
vs.unwrap()
|
||||
)
|
||||
)
|
||||
}
|
||||
"index" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_indexs(v.as_str())
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_indexs(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
).collect();
|
||||
vs.unwrap()
|
||||
)
|
||||
)
|
||||
}
|
||||
"acp_targetscope" | "acp_receiver" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_json_filter_s(v.as_str())
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_json_filter_s(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
).collect();
|
||||
vs.unwrap()
|
||||
)
|
||||
)
|
||||
}
|
||||
"displayname" | "description" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_utf8(v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_utf8(v))
|
||||
)
|
||||
}
|
||||
"spn" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| {
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| {
|
||||
Value::new_spn_parse(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect SPN attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
}).collect();
|
||||
vs.unwrap()
|
||||
})
|
||||
)
|
||||
}
|
||||
"gidnumber" | "version" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| {
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| {
|
||||
Value::new_uint32_str(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect UINT32 attribute to be presented UTF8 string");
|
||||
Value::new_utf8(v)
|
||||
})
|
||||
}).collect();
|
||||
vs.unwrap()
|
||||
})
|
||||
)
|
||||
}
|
||||
"domain_token_key" | "fernet_private_key_str" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_secret_str(&v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_secret_str(&v))
|
||||
)
|
||||
}
|
||||
"es256_private_key_der" => {
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_privatebinary_base64(&v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_privatebinary_base64(&v))
|
||||
)
|
||||
}
|
||||
ia => {
|
||||
warn!("WARNING: Allowing invalid attribute {} to be interpretted as UTF8 string. YOU MAY ENCOUNTER ODD BEHAVIOUR!!!", ia);
|
||||
let vs: Option<ValueSet> = vs.into_iter().map(|v| Value::new_utf8(v)).collect();
|
||||
vs.unwrap()
|
||||
valueset::from_value_iter(
|
||||
vs.into_iter().map(|v| Value::new_utf8(v))
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
.unwrap();
|
||||
Some((attr, vv))
|
||||
}
|
||||
})
|
||||
|
@ -541,7 +565,7 @@ impl Entry<EntryInit, EntryNew> {
|
|||
|
||||
impl<STATE> Entry<EntryInvalid, STATE> {
|
||||
// This is only used in tests today, but I don't want to cfg test it.
|
||||
pub(crate) fn get_uuid(&self) -> Option<&Uuid> {
|
||||
pub(crate) fn get_uuid(&self) -> Option<Uuid> {
|
||||
self.attrs.get("uuid").and_then(|vs| vs.to_uuid_single())
|
||||
}
|
||||
|
||||
|
@ -560,7 +584,6 @@ impl<STATE> Entry<EntryInvalid, STATE> {
|
|||
.ok_or_else(|| SchemaError::MissingMustAttribute(vec!["uuid".to_string()]))
|
||||
.and_then(|vs| {
|
||||
vs.to_uuid_single()
|
||||
.copied()
|
||||
.ok_or_else(|| SchemaError::MissingMustAttribute(vec!["uuid".to_string()]))
|
||||
})?;
|
||||
|
||||
|
@ -592,7 +615,7 @@ impl<STATE> Entry<EntryInvalid, STATE> {
|
|||
|
||||
let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
|
||||
|
||||
match entry_classes.as_classname_iter() {
|
||||
match entry_classes.as_iutf8_iter() {
|
||||
Some(cls_iter) => cls_iter.for_each(|s| match schema_classes.get(s) {
|
||||
Some(x) => classes.push(x),
|
||||
None => invalid_classes.push(s.to_string()),
|
||||
|
@ -925,12 +948,12 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
// into proper structures, and they themself emit/modify entries?
|
||||
|
||||
DbEntry {
|
||||
ent: DbEntryVers::V1(DbEntryV1 {
|
||||
ent: DbEntryVers::V2(DbEntryV2 {
|
||||
attrs: self
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|(k, vs)| {
|
||||
let dbvs: Vec<_> = vs.to_db_valuev1_iter().collect();
|
||||
let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
|
||||
(k.clone(), dbvs)
|
||||
})
|
||||
.collect(),
|
||||
|
@ -963,7 +986,7 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
.get("spn")
|
||||
.and_then(|vs| vs.to_value_single())
|
||||
.or_else(|| self.attrs.get("name").and_then(|vs| vs.to_value_single()))
|
||||
.unwrap_or_else(|| Value::new_uuidr(self.get_uuid()))
|
||||
.unwrap_or_else(|| Value::new_uuid(self.get_uuid()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1227,34 +1250,74 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
}
|
||||
(Some(pre_vs), Some(post_vs)) => {
|
||||
// it exists in both, we need to work out the differents within the attr.
|
||||
let removed_vs = pre_vs.idx_eq_key_difference(post_vs);
|
||||
let added_vs = post_vs.idx_eq_key_difference(pre_vs);
|
||||
|
||||
let mut diff = Vec::with_capacity(
|
||||
removed_vs.as_ref().map(|v| v.len()).unwrap_or(0)
|
||||
+ added_vs.as_ref().map(|v| v.len()).unwrap_or(0),
|
||||
);
|
||||
let mut pre_idx_keys = pre_vs.generate_idx_eq_keys();
|
||||
pre_idx_keys.sort_unstable();
|
||||
let mut post_idx_keys = post_vs.generate_idx_eq_keys();
|
||||
post_idx_keys.sort_unstable();
|
||||
|
||||
let sz = if pre_idx_keys.len() > post_idx_keys.len() {
|
||||
pre_idx_keys.len()
|
||||
} else {
|
||||
post_idx_keys.len()
|
||||
};
|
||||
|
||||
let mut pre_iter = pre_idx_keys.iter();
|
||||
let mut post_iter = post_idx_keys.iter();
|
||||
|
||||
let mut pre = pre_iter.next();
|
||||
let mut post = post_iter.next();
|
||||
|
||||
let mut added_vs = Vec::with_capacity(sz);
|
||||
|
||||
let mut removed_vs = Vec::with_capacity(sz);
|
||||
|
||||
loop {
|
||||
match (pre, post) {
|
||||
(Some(a), Some(b)) => {
|
||||
match a.cmp(b) {
|
||||
Ordering::Less => {
|
||||
removed_vs.push(a.clone());
|
||||
pre = pre_iter.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
// In both - no action needed.
|
||||
pre = pre_iter.next();
|
||||
post = post_iter.next();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
added_vs.push(b.clone());
|
||||
post = post_iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(a), None) => {
|
||||
removed_vs.push(a.clone());
|
||||
pre = pre_iter.next();
|
||||
}
|
||||
(None, Some(b)) => {
|
||||
added_vs.push(b.clone());
|
||||
post = post_iter.next();
|
||||
}
|
||||
(None, None) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut diff =
|
||||
Vec::with_capacity(removed_vs.len() + added_vs.len());
|
||||
|
||||
match ikey.itype {
|
||||
IndexType::Equality => {
|
||||
if let Some(removed_vs) = removed_vs {
|
||||
removed_vs
|
||||
.generate_idx_eq_keys()
|
||||
.into_iter()
|
||||
.map(|idx_key| {
|
||||
Err((&ikey.attr, &ikey.itype, idx_key))
|
||||
})
|
||||
.for_each(|v| diff.push(v));
|
||||
}
|
||||
if let Some(added_vs) = added_vs {
|
||||
added_vs
|
||||
.generate_idx_eq_keys()
|
||||
.into_iter()
|
||||
.map(|idx_key| {
|
||||
Ok((&ikey.attr, &ikey.itype, idx_key))
|
||||
})
|
||||
.for_each(|v| diff.push(v));
|
||||
}
|
||||
removed_vs
|
||||
.into_iter()
|
||||
.map(|idx_key| Err((&ikey.attr, &ikey.itype, idx_key)))
|
||||
.for_each(|v| diff.push(v));
|
||||
added_vs
|
||||
.into_iter()
|
||||
.map(|idx_key| Ok((&ikey.attr, &ikey.itype, idx_key)))
|
||||
.for_each(|v| diff.push(v));
|
||||
}
|
||||
IndexType::Presence => {
|
||||
// No action - we still are "present", so nothing to do!
|
||||
|
@ -1275,29 +1338,28 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
|
||||
// Convert attrs from db format to value
|
||||
let r_attrs: Result<Map<AttrString, ValueSet>, ()> = match db_e.ent {
|
||||
DbEntryVers::V1(v1) => v1
|
||||
DbEntryVers::V1(_) => {
|
||||
admin_error!("Db V1 entry should have been migrated!");
|
||||
Err(())
|
||||
}
|
||||
DbEntryVers::V2(v2) => v2
|
||||
.attrs
|
||||
.into_iter()
|
||||
// Skip anything empty as new VS can't deal with it.
|
||||
.filter(|(_k, vs)| !vs.is_empty())
|
||||
.map(|(k, vs)| {
|
||||
let vv: Result<ValueSet, _> = ValueSet::from_db_valuev1_iter(vs.into_iter());
|
||||
match vv {
|
||||
Ok(vv) => Ok((k, vv)),
|
||||
Err(e) => {
|
||||
admin_error!(?e, value = ?k, "from_dbentry failed");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
.map(|(k, dbvs)| {
|
||||
valueset::from_db_valueset_v2(dbvs)
|
||||
.map(|vs: ValueSet| (k, vs))
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "from_dbentry failed");
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let attrs = r_attrs.ok()?;
|
||||
|
||||
let uuid = attrs
|
||||
.get("uuid")
|
||||
.and_then(|vs| vs.to_uuid_single().copied())?;
|
||||
let uuid = attrs.get("uuid").and_then(|vs| vs.to_uuid_single())?;
|
||||
|
||||
Some(Entry {
|
||||
valid: EntrySealed { uuid },
|
||||
|
@ -1356,16 +1418,12 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
/// Convert this recycled entry, into a tombstone ready for reaping.
|
||||
pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
|
||||
// Duplicate this to a tombstone entry
|
||||
let class_ava =
|
||||
unsafe { valueset![Value::new_class("object"), Value::new_class("tombstone")] };
|
||||
let last_mod_ava = ValueSet::new(Value::new_cid(cid.clone()));
|
||||
let class_ava = vs_iutf8!["object", "tombstone"];
|
||||
let last_mod_ava = vs_cid![cid.clone()];
|
||||
|
||||
let mut attrs_new: Map<AttrString, ValueSet> = Map::new();
|
||||
|
||||
attrs_new.insert(
|
||||
AttrString::from("uuid"),
|
||||
ValueSet::new(Value::new_uuidr(self.get_uuid())),
|
||||
);
|
||||
attrs_new.insert(AttrString::from("uuid"), vs_uuid![self.get_uuid()]);
|
||||
attrs_new.insert(AttrString::from("class"), class_ava);
|
||||
attrs_new.insert(AttrString::from("last_modified_cid"), last_mod_ava);
|
||||
|
||||
|
@ -1411,8 +1469,8 @@ impl<STATE> Entry<EntryValid, STATE> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_uuid(&self) -> &Uuid {
|
||||
&self.valid.uuid
|
||||
pub fn get_uuid(&self) -> Uuid {
|
||||
self.valid.uuid
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1429,8 +1487,8 @@ impl<STATE> Entry<EntrySealed, STATE> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_uuid(&self) -> &Uuid {
|
||||
&self.valid.uuid
|
||||
pub fn get_uuid(&self) -> Uuid {
|
||||
self.valid.uuid
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1447,8 +1505,8 @@ impl<STATE> Entry<EntrySealed, STATE> {
|
|||
}
|
||||
|
||||
impl Entry<EntryReduced, EntryCommitted> {
|
||||
pub fn get_uuid(&self) -> &Uuid {
|
||||
&self.valid.uuid
|
||||
pub fn get_uuid(&self) -> Uuid {
|
||||
self.valid.uuid
|
||||
}
|
||||
|
||||
/// Transform this reduced entry into a JSON protocol form that can be sent to clients.
|
||||
|
@ -1544,30 +1602,38 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
let r = vs.insert_checked(value);
|
||||
debug_assert!(r.is_ok());
|
||||
} else {
|
||||
self.attrs
|
||||
.insert(AttrString::from(attr), ValueSet::new(value));
|
||||
let vs = valueset::from_value_iter(std::iter::once(value))
|
||||
.expect("Unable to fail - not empty, and only one type!");
|
||||
self.attrs.insert(AttrString::from(attr), vs);
|
||||
}
|
||||
// Doesn't matter if it already exists, equality will replace.
|
||||
}
|
||||
|
||||
/// Overwrite the current set of values for an attribute, with this new set.
|
||||
// pub fn set_ava_int(&mut self, attr: &str, values: Set<Value>) {
|
||||
pub fn set_ava_int<T>(&mut self, attr: &str, iter: T)
|
||||
where
|
||||
T: IntoIterator<Item = Value>,
|
||||
{
|
||||
// Overwrite the existing value, build a tree from the list.
|
||||
let values: Option<ValueSet> = iter.into_iter().collect();
|
||||
if let Some(vs) = values {
|
||||
let _ = self.attrs.insert(AttrString::from(attr), vs);
|
||||
} else {
|
||||
self.attrs.remove(attr);
|
||||
let values = valueset::from_value_iter(iter.into_iter());
|
||||
match values {
|
||||
Ok(vs) => {
|
||||
let _ = self.attrs.insert(AttrString::from(attr), vs);
|
||||
}
|
||||
Err(e) => {
|
||||
admin_warn!(
|
||||
"dropping content of {} due to invalid valueset {:?}",
|
||||
attr,
|
||||
e
|
||||
);
|
||||
self.attrs.remove(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the last_changed flag of this entry to the given change identifier.
|
||||
fn set_last_changed(&mut self, cid: Cid) {
|
||||
let cv = valueset![Value::new_cid(cid)];
|
||||
let cv = vs_cid![cid];
|
||||
let _ = self.attrs.insert(AttrString::from("last_modified_cid"), cv);
|
||||
}
|
||||
|
||||
|
@ -1610,18 +1676,24 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
&self,
|
||||
attr: &str,
|
||||
) -> Option<&std::collections::BTreeMap<Uuid, IntentTokenState>> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.as_intenttoken())
|
||||
self.attrs.get(attr).and_then(|vs| vs.as_intenttoken_map())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// 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>> {
|
||||
self.get_ava_set(attr).and_then(|vs| vs.as_str_iter())
|
||||
pub fn get_ava_iter_iname(&self, attr: &str) -> Option<impl Iterator<Item = &str>> {
|
||||
self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// If possible, return an iterator over the set of values transformed into a `&Uuid`.
|
||||
pub fn get_ava_as_refuuid(&self, attr: &str) -> Option<Box<dyn Iterator<Item = &Uuid> + '_>> {
|
||||
/// If possible, return an iterator over the set of values transformed into a `&str`.
|
||||
pub fn get_ava_iter_iutf8(&self, attr: &str) -> Option<impl Iterator<Item = &str>> {
|
||||
self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// If possible, return an iterator over the set of values transformed into a `Uuid`.
|
||||
pub fn get_ava_as_refuuid(&self, attr: &str) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
|
||||
// If any value is NOT a reference, it's filtered out.
|
||||
self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
|
||||
}
|
||||
|
@ -1645,7 +1717,7 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
#[inline(always)]
|
||||
pub(crate) fn get_ava_opt_index(&self, attr: &str) -> Option<Vec<IndexType>> {
|
||||
if let Some(vs) = self.get_ava_set(attr) {
|
||||
vs.as_indextype_set().map(|i| i.cloned().collect())
|
||||
vs.as_indextype_iter().map(|i| i.collect())
|
||||
} else {
|
||||
// Empty, but consider as valid.
|
||||
Some(vec![])
|
||||
|
@ -1679,7 +1751,7 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
|
||||
#[inline(always)]
|
||||
/// Return a single syntax type, if valid to transform this value.
|
||||
pub fn get_ava_single_syntax(&self, attr: &str) -> Option<&SyntaxType> {
|
||||
pub fn get_ava_single_syntax(&self, attr: &str) -> Option<SyntaxType> {
|
||||
self.attrs
|
||||
.get(attr)
|
||||
.and_then(|vs| vs.to_syntaxtype_single())
|
||||
|
@ -1707,8 +1779,20 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
|
||||
#[inline(always)]
|
||||
/// Return a single `&str`, if valid to transform this value.
|
||||
pub fn get_ava_single_str(&self, attr: &str) -> Option<&str> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_str_single())
|
||||
pub(crate) fn get_ava_single_utf8(&self, attr: &str) -> Option<&str> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_utf8_single())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Return a single `&str`, if valid to transform this value.
|
||||
pub(crate) fn get_ava_single_iutf8(&self, attr: &str) -> Option<&str> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_iutf8_single())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Return a single `&str`, if valid to transform this value.
|
||||
pub(crate) fn get_ava_single_iname(&self, attr: &str) -> Option<&str> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_iname_single())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -1717,11 +1801,11 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
self.attrs.get(attr).and_then(|vs| vs.to_url_single())
|
||||
}
|
||||
|
||||
pub fn get_ava_single_uuid(&self, attr: &str) -> Option<&Uuid> {
|
||||
pub fn get_ava_single_uuid(&self, attr: &str) -> Option<Uuid> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_uuid_single())
|
||||
}
|
||||
|
||||
pub fn get_ava_single_refer(&self, attr: &str) -> Option<&Uuid> {
|
||||
pub fn get_ava_single_refer(&self, attr: &str) -> Option<Uuid> {
|
||||
self.attrs.get(attr).and_then(|vs| vs.to_refer_single())
|
||||
}
|
||||
|
||||
|
@ -1752,7 +1836,7 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
#[inline(always)]
|
||||
/// Return a single security principle name, if valid to transform this value.
|
||||
pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<Value> {
|
||||
self.get_ava_single_str("name")
|
||||
self.get_ava_single_iname("name")
|
||||
.map(|name| Value::new_spn_str(name, domain_name))
|
||||
}
|
||||
|
||||
|
@ -2050,16 +2134,16 @@ impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
|
|||
impl From<&SchemaAttribute> for Entry<EntryInit, EntryNew> {
|
||||
fn from(s: &SchemaAttribute) -> Self {
|
||||
// Convert an Attribute to an entry ... make it good!
|
||||
let uuid_v = ValueSet::new(Value::new_uuidr(&s.uuid));
|
||||
let name_v = ValueSet::new(Value::new_iutf8(s.name.as_str()));
|
||||
let desc_v = ValueSet::new(Value::new_utf8(s.description.clone()));
|
||||
let uuid_v = vs_uuid![s.uuid];
|
||||
let name_v = vs_iutf8![s.name.as_str()];
|
||||
let desc_v = vs_utf8![s.description.clone()];
|
||||
|
||||
let multivalue_v = ValueSet::new(Value::from(s.multivalue));
|
||||
let unique_v = ValueSet::new(Value::from(s.unique));
|
||||
let multivalue_v = vs_bool![s.multivalue];
|
||||
let unique_v = vs_bool![s.unique];
|
||||
|
||||
let index_v: Option<ValueSet> = s.index.iter().cloned().map(Value::from).collect();
|
||||
let index_v = ValueSetIndex::from_iter(s.index.iter().copied());
|
||||
|
||||
let syntax_v = ValueSet::new(Value::from(s.syntax.clone()));
|
||||
let syntax_v = vs_syntax![s.syntax];
|
||||
|
||||
// Build the Map of the attributes relevant
|
||||
// let mut attrs: Map<AttrString, Set<Value>> = Map::with_capacity(8);
|
||||
|
@ -2073,13 +2157,10 @@ impl From<&SchemaAttribute> for Entry<EntryInit, EntryNew> {
|
|||
attrs.insert(AttrString::from("index"), vs);
|
||||
}
|
||||
attrs.insert(AttrString::from("syntax"), syntax_v);
|
||||
attrs.insert(AttrString::from("class"), unsafe {
|
||||
valueset![
|
||||
Value::new_class("object"),
|
||||
Value::new_class("system"),
|
||||
Value::new_class("attributetype")
|
||||
]
|
||||
});
|
||||
attrs.insert(
|
||||
AttrString::from("class"),
|
||||
vs_iutf8!["object", "system", "attributetype"],
|
||||
);
|
||||
|
||||
// Insert stuff.
|
||||
|
||||
|
@ -2093,37 +2174,27 @@ impl From<&SchemaAttribute> for Entry<EntryInit, EntryNew> {
|
|||
|
||||
impl From<&SchemaClass> for Entry<EntryInit, EntryNew> {
|
||||
fn from(s: &SchemaClass) -> Self {
|
||||
let uuid_v = ValueSet::new(Value::new_uuidr(&s.uuid));
|
||||
let name_v = ValueSet::new(Value::new_iutf8(s.name.as_str()));
|
||||
let desc_v = ValueSet::new(Value::new_utf8(s.description.clone()));
|
||||
let uuid_v = vs_uuid![s.uuid];
|
||||
let name_v = vs_iutf8![s.name.as_str()];
|
||||
let desc_v = vs_utf8![s.description.clone()];
|
||||
|
||||
// let mut attrs: Map<AttrString, Set<Value>> = Map::with_capacity(8);
|
||||
let mut attrs: Map<AttrString, ValueSet> = Map::new();
|
||||
attrs.insert(AttrString::from("classname"), name_v);
|
||||
attrs.insert(AttrString::from("description"), desc_v);
|
||||
attrs.insert(AttrString::from("uuid"), uuid_v);
|
||||
attrs.insert(AttrString::from("class"), unsafe {
|
||||
valueset![
|
||||
Value::new_class("object"),
|
||||
Value::new_class("system"),
|
||||
Value::new_class("classtype")
|
||||
]
|
||||
});
|
||||
attrs.insert(
|
||||
AttrString::from("class"),
|
||||
vs_iutf8!["object", "system", "classtype"],
|
||||
);
|
||||
|
||||
let vs_systemmay: Option<ValueSet> = s
|
||||
.systemmay
|
||||
.iter()
|
||||
.map(|sm| Value::new_attr(sm.as_str()))
|
||||
.collect();
|
||||
let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
|
||||
if let Some(vs) = vs_systemmay {
|
||||
attrs.insert(AttrString::from("systemmay"), vs);
|
||||
}
|
||||
|
||||
let vs_systemmust: Option<ValueSet> = s
|
||||
.systemmust
|
||||
.iter()
|
||||
.map(|sm| Value::new_attr(sm.as_str()))
|
||||
.collect();
|
||||
let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
|
||||
|
||||
if let Some(vs) = vs_systemmust {
|
||||
attrs.insert(AttrString::from("systemmust"), vs);
|
||||
}
|
||||
|
|
|
@ -1822,34 +1822,34 @@ mod tests {
|
|||
assert!(cr.is_ok());
|
||||
|
||||
// Resolving most times should yield expected results
|
||||
let t1 = ValueSet::new(Value::new_utf8s("teststring"));
|
||||
let t1 = vs_utf8!["teststring".to_string()] as _;
|
||||
let r1 = server_txn.resolve_valueset(&t1);
|
||||
assert!(r1 == Ok(vec!["teststring".to_string()]));
|
||||
|
||||
// Resolve UUID with matching spn
|
||||
let t_uuid =
|
||||
ValueSet::new(Value::new_refer_s("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap());
|
||||
vs_refer![Uuid::parse_str("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap()] as _;
|
||||
let r_uuid = server_txn.resolve_valueset(&t_uuid);
|
||||
debug!("{:?}", r_uuid);
|
||||
assert!(r_uuid == Ok(vec!["testperson1@example.com".to_string()]));
|
||||
|
||||
// Resolve UUID with matching name
|
||||
let t_uuid =
|
||||
ValueSet::new(Value::new_refer_s("a67c0c71-0b35-4218-a6b0-22d23d131d27").unwrap());
|
||||
vs_refer![Uuid::parse_str("a67c0c71-0b35-4218-a6b0-22d23d131d27").unwrap()] as _;
|
||||
let r_uuid = server_txn.resolve_valueset(&t_uuid);
|
||||
debug!("{:?}", r_uuid);
|
||||
assert!(r_uuid == Ok(vec!["testperson2".to_string()]));
|
||||
|
||||
// Resolve UUID non-exist
|
||||
let t_uuid_non =
|
||||
ValueSet::new(Value::new_refer_s("b83e98f0-3d2e-41d2-9796-d8d993289c86").unwrap());
|
||||
vs_refer![Uuid::parse_str("b83e98f0-3d2e-41d2-9796-d8d993289c86").unwrap()] as _;
|
||||
let r_uuid_non = server_txn.resolve_valueset(&t_uuid_non);
|
||||
debug!("{:?}", r_uuid_non);
|
||||
assert!(r_uuid_non == Ok(vec!["b83e98f0-3d2e-41d2-9796-d8d993289c86".to_string()]));
|
||||
|
||||
// Resolve UUID to tombstone/recycled (same an non-exst)
|
||||
let t_uuid_ts =
|
||||
ValueSet::new(Value::new_refer_s("9557f49c-97a5-4277-a9a5-097d17eb8317").unwrap());
|
||||
vs_refer![Uuid::parse_str("9557f49c-97a5-4277-a9a5-097d17eb8317").unwrap()] as _;
|
||||
let r_uuid_ts = server_txn.resolve_valueset(&t_uuid_ts);
|
||||
debug!("{:?}", r_uuid_ts);
|
||||
assert!(r_uuid_ts == Ok(vec!["9557f49c-97a5-4277-a9a5-097d17eb8317".to_string()]));
|
||||
|
|
|
@ -69,7 +69,7 @@ impl From<&IdentType> for IdentityId {
|
|||
fn from(idt: &IdentType) -> Self {
|
||||
match idt {
|
||||
IdentType::Internal => IdentityId::Internal,
|
||||
IdentType::User(u) => IdentityId::User(*u.entry.get_uuid()),
|
||||
IdentType::User(u) => IdentityId::User(u.entry.get_uuid()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ impl Identity {
|
|||
pub fn get_uuid(&self) -> Option<Uuid> {
|
||||
match &self.origin {
|
||||
IdentType::Internal => None,
|
||||
IdentType::User(u) => Some(*u.entry.get_uuid()),
|
||||
IdentType::User(u) => Some(u.entry.get_uuid()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,14 +36,14 @@ macro_rules! try_from_entry {
|
|||
|
||||
// Now extract our needed attributes
|
||||
let name = $value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: name".to_string(),
|
||||
))?;
|
||||
|
||||
let displayname = $value
|
||||
.get_ava_single_str("displayname")
|
||||
.get_ava_single_utf8("displayname")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: displayname".to_string(),
|
||||
|
|
|
@ -59,6 +59,7 @@ struct CredentialUpdateSessionTokenInner {
|
|||
pub max_ttl: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CredentialUpdateSessionToken {
|
||||
pub token_enc: String,
|
||||
}
|
||||
|
@ -373,7 +374,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
|
@ -485,7 +486,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
|
@ -604,7 +605,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
|
@ -1229,6 +1230,7 @@ mod tests {
|
|||
// Already used.
|
||||
let cur = idms_prox_write.exchange_intent_credential_update(intent_tok, ct);
|
||||
|
||||
trace!(?cur);
|
||||
assert!(cur.is_err());
|
||||
})
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ pub struct Group {
|
|||
macro_rules! try_from_account_e {
|
||||
($value:expr, $qs:expr) => {{
|
||||
let name = $value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(str::to_string)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *$value.get_uuid();
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let upg = Group { name, uuid };
|
||||
|
||||
|
@ -36,7 +36,7 @@ macro_rules! try_from_account_e {
|
|||
// just give and empty result set.
|
||||
let f = filter!(f_or(
|
||||
riter
|
||||
.map(|u| f_eq("uuid", PartialValue::new_uuidr(u)))
|
||||
.map(|u| f_eq("uuid", PartialValue::new_uuid(u)))
|
||||
.collect()
|
||||
));
|
||||
let ges: Vec<_> = $qs.internal_search(f).map_err(|e| {
|
||||
|
@ -96,13 +96,13 @@ impl Group {
|
|||
|
||||
// Now extract our needed attributes
|
||||
let name = value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *value.get_uuid();
|
||||
let uuid = value.get_uuid();
|
||||
|
||||
Ok(Group { name, uuid })
|
||||
}
|
||||
|
|
|
@ -260,7 +260,7 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
let rs_set: Result<HashMap<_, _>, _> = value
|
||||
.into_iter()
|
||||
.map(|ent| {
|
||||
let uuid = *ent.get_uuid();
|
||||
let uuid = ent.get_uuid();
|
||||
admin_info!(?uuid, "Checking oauth2 configuration");
|
||||
// From each entry, attempt to make an oauth2 configuration.
|
||||
if !ent.attribute_equality("class", &CLASS_OAUTH2) {
|
||||
|
@ -272,12 +272,12 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
// Now we know we can load the attrs.
|
||||
trace!("name");
|
||||
let name = ent
|
||||
.get_ava_single_str("oauth2_rs_name")
|
||||
.get_ava_single_iname("oauth2_rs_name")
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
trace!("displayname");
|
||||
let displayname = ent
|
||||
.get_ava_single_str("displayname")
|
||||
.get_ava_single_utf8("displayname")
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
trace!("origin");
|
||||
|
@ -287,7 +287,7 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
.ok_or(OperationError::InvalidValueState)?;
|
||||
trace!("authz_secret");
|
||||
let authz_secret = ent
|
||||
.get_ava_single_str("oauth2_rs_basic_secret")
|
||||
.get_ava_single_utf8("oauth2_rs_basic_secret")
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
trace!("token_key");
|
||||
|
@ -1347,7 +1347,7 @@ mod tests {
|
|||
.internal_search_uuid(&uuid)
|
||||
.expect("Failed to retrieve oauth2 resource entry ");
|
||||
let secret = entry
|
||||
.get_ava_single_str("oauth2_rs_basic_secret")
|
||||
.get_ava_single_utf8("oauth2_rs_basic_secret")
|
||||
.map(str::to_string)
|
||||
.expect("No oauth2_rs_basic_secret found");
|
||||
|
||||
|
|
|
@ -44,16 +44,16 @@ impl RadiusAccount {
|
|||
.to_string();
|
||||
|
||||
let name = value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *value.get_uuid();
|
||||
let uuid = value.get_uuid();
|
||||
|
||||
let displayname = value
|
||||
.get_ava_single_str("displayname")
|
||||
.get_ava_single_utf8("displayname")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: displayname".to_string())
|
||||
|
|
|
@ -1372,9 +1372,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
.qs_write
|
||||
.impersonate_modify_gen_event(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(pce.target))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(pce.target))),
|
||||
&modlist,
|
||||
&pce.ident,
|
||||
)
|
||||
|
@ -1451,9 +1451,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
.qs_write
|
||||
.impersonate_modify_gen_event(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(pce.target))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(pce.target))),
|
||||
&modlist,
|
||||
&pce.ident,
|
||||
)
|
||||
|
@ -1518,7 +1518,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.internal_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(target))),
|
||||
&modlist,
|
||||
)
|
||||
.map_err(|e| {
|
||||
|
@ -1557,9 +1557,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&gpe.target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(gpe.target))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&gpe.target))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(gpe.target))),
|
||||
&modlist,
|
||||
// Provide the event to impersonate
|
||||
&gpe.ident,
|
||||
|
@ -1595,9 +1595,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&gbe.target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(gbe.target))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&gbe.target))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(gbe.target))),
|
||||
&modlist,
|
||||
// Provide the event to impersonate
|
||||
&gbe.ident,
|
||||
|
@ -1624,9 +1624,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
&rte.ident,
|
||||
)
|
||||
|
@ -1660,9 +1660,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&rrse.target))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(rrse.target))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&rrse.target))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(rrse.target))),
|
||||
&modlist,
|
||||
// Provide the event to impersonate
|
||||
&rrse.ident,
|
||||
|
@ -1731,9 +1731,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
&modlist,
|
||||
&wre.ident,
|
||||
)
|
||||
|
@ -1768,9 +1768,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
&rwe.ident,
|
||||
)
|
||||
|
@ -1843,9 +1843,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
&modlist,
|
||||
&vte.ident,
|
||||
)
|
||||
|
@ -1897,9 +1897,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&session.account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(session.account.uuid))),
|
||||
&modlist,
|
||||
&aste.ident,
|
||||
)
|
||||
|
@ -1928,9 +1928,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
self.qs_write
|
||||
.impersonate_modify(
|
||||
// Filter as executed
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
// Filter as intended (acp)
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&account.uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(account.uuid))),
|
||||
&modlist,
|
||||
&rte.ident,
|
||||
)
|
||||
|
@ -1959,7 +1959,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
})?;
|
||||
|
||||
self.qs_write.internal_modify(
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pwu.target_uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(pwu.target_uuid))),
|
||||
&modlist,
|
||||
)
|
||||
} else {
|
||||
|
@ -1991,7 +1991,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
})?;
|
||||
|
||||
self.qs_write.internal_modify(
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pwu.target_uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(pwu.target_uuid))),
|
||||
&modlist,
|
||||
)
|
||||
} else {
|
||||
|
@ -2015,7 +2015,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
if let Some(modlist) = opt_modlist {
|
||||
self.qs_write.internal_modify(
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&wci.target_uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(wci.target_uuid))),
|
||||
&modlist,
|
||||
)
|
||||
} else {
|
||||
|
@ -2039,7 +2039,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
})?;
|
||||
|
||||
self.qs_write.internal_modify(
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuidr(&bcr.target_uuid))),
|
||||
&filter_all!(f_eq("uuid", PartialValue::new_uuid(bcr.target_uuid))),
|
||||
&modlist,
|
||||
)
|
||||
}
|
||||
|
@ -3996,7 +3996,7 @@ mod tests {
|
|||
let idms_prox_write = idms.proxy_write(ct.clone());
|
||||
let me_reset_tokens = unsafe {
|
||||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq("uuid", PartialValue::new_uuidr(&UUID_DOMAIN_INFO))),
|
||||
filter!(f_eq("uuid", PartialValue::new_uuid(*UUID_DOMAIN_INFO))),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Purged(AttrString::from("fernet_private_key_str")),
|
||||
Modify::Purged(AttrString::from("es256_private_key_der")),
|
||||
|
|
|
@ -55,7 +55,7 @@ macro_rules! try_from_entry {
|
|||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
|
@ -65,10 +65,10 @@ macro_rules! try_from_entry {
|
|||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *$value.get_uuid();
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let displayname = $value
|
||||
.get_ava_single_str("displayname")
|
||||
.get_ava_single_utf8("displayname")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: displayname".to_string())
|
||||
|
@ -79,7 +79,7 @@ macro_rules! try_from_entry {
|
|||
})?;
|
||||
|
||||
let shell = $value
|
||||
.get_ava_single_str("loginshell")
|
||||
.get_ava_single_iutf8("loginshell")
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let sshkeys = $value
|
||||
|
@ -307,7 +307,7 @@ macro_rules! try_from_group_e {
|
|||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
|
@ -317,7 +317,7 @@ macro_rules! try_from_group_e {
|
|||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *$value.get_uuid();
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string())
|
||||
|
@ -350,7 +350,7 @@ macro_rules! try_from_account_group_e {
|
|||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
|
@ -360,7 +360,7 @@ macro_rules! try_from_account_group_e {
|
|||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
})?;
|
||||
|
||||
let uuid = *$value.get_uuid();
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string())
|
||||
|
@ -381,7 +381,7 @@ macro_rules! try_from_account_group_e {
|
|||
f_eq("class", PartialValue::new_class("group")),
|
||||
f_or(
|
||||
riter
|
||||
.map(|u| f_eq("uuid", PartialValue::new_uuidr(u)))
|
||||
.map(|u| f_eq("uuid", PartialValue::new_uuid(u)))
|
||||
.collect()
|
||||
)
|
||||
]));
|
||||
|
|
|
@ -52,7 +52,7 @@ impl LdapServer {
|
|||
.internal_search_uuid(&UUID_DOMAIN_INFO)?;
|
||||
|
||||
let domain_name = domain_entry
|
||||
.get_ava_single_str("domain_name")
|
||||
.get_ava_single_iname("domain_name")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidEntryState)?;
|
||||
|
||||
|
|
|
@ -87,7 +87,11 @@ pub mod prelude {
|
|||
QueryServerWriteTransaction,
|
||||
};
|
||||
pub use crate::value::{IndexType, PartialValue, SyntaxType, Value};
|
||||
pub use crate::valueset::ValueSet;
|
||||
pub use crate::valueset::{
|
||||
ValueSet, ValueSetBool, ValueSetCid, ValueSetIndex, ValueSetIutf8, ValueSetRefer,
|
||||
ValueSetSecret, ValueSetSpn, ValueSetSyntax, ValueSetT, ValueSetUint32, ValueSetUtf8,
|
||||
ValueSetUuid,
|
||||
};
|
||||
pub use crate::{
|
||||
admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace, filter_warn,
|
||||
perf_trace, request_error, request_info, request_trace, request_warn, security_access,
|
||||
|
|
|
@ -151,7 +151,7 @@ macro_rules! entry_str_to_account {
|
|||
unsafe { Entry::unsafe_from_entry_str($entry_str).into_invalid_new() };
|
||||
// Add spn, because normally this is generated but in tests we can't.
|
||||
let spn = e
|
||||
.get_ava_single_str("name")
|
||||
.get_ava_single_iname("name")
|
||||
.map(|s| Value::new_spn_str(s, "example.com"))
|
||||
.expect("Failed to munge spn from name!");
|
||||
e.set_ava("spn", once(spn));
|
||||
|
@ -569,25 +569,6 @@ macro_rules! btreemap {
|
|||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! valueset {
|
||||
() => (
|
||||
compile_error!("ValueSet needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSet::new($e)
|
||||
});
|
||||
($e:expr,) => ({
|
||||
valueset!($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x: ValueSet = ValueSet::new($e);
|
||||
$(assert!(x.insert($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! entry_init {
|
||||
|
@ -607,3 +588,177 @@ macro_rules! entry_init {
|
|||
e1
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! mergesets {
|
||||
(
|
||||
$a:expr,
|
||||
$b:expr
|
||||
) => {{
|
||||
$b.iter().for_each(|v| {
|
||||
$a.insert(v.clone());
|
||||
});
|
||||
Ok(())
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! mergemaps {
|
||||
(
|
||||
$a:expr,
|
||||
$b:expr
|
||||
) => {{
|
||||
$b.iter().for_each(|(k, v)| {
|
||||
if !$a.contains_key(k) {
|
||||
$a.insert(k.clone(), v.clone());
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_utf8 {
|
||||
() => (
|
||||
compile_error!("ValueSetUtf8 needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetUtf8::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetUtf8::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_iutf8 {
|
||||
() => (
|
||||
compile_error!("ValueSetIutf8 needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetIutf8::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetIutf8::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_iname {
|
||||
() => (
|
||||
compile_error!("ValueSetIname needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetIname::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetIname::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_uuid {
|
||||
() => (
|
||||
compile_error!("ValueSetUuid needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetUuid::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetUuid::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_refer {
|
||||
() => (
|
||||
compile_error!("ValueSetRefer needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetRefer::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetRefer::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_bool {
|
||||
() => (
|
||||
compile_error!("ValueSetBool needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetBool::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetBool::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_syntax {
|
||||
() => (
|
||||
compile_error!("ValueSetSyntax needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetSyntax::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetSyntax::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_index {
|
||||
() => (
|
||||
compile_error!("ValueSetIndex needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetIndex::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetIndex::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! vs_cid {
|
||||
() => (
|
||||
compile_error!("ValueSetCid needs at least 1 element")
|
||||
);
|
||||
($e:expr) => ({
|
||||
ValueSetCid::new($e)
|
||||
});
|
||||
($e:expr, $($item:expr),*) => ({
|
||||
let mut x = ValueSetCid::new($e);
|
||||
$(assert!(x.push($item));)*
|
||||
x
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,12 +23,9 @@ fn get_cand_attr_set<VALID, STATE>(
|
|||
|
||||
cand.iter()
|
||||
.try_for_each(|e| {
|
||||
let uuid = match e.get_ava_single_uuid("uuid") {
|
||||
Some(v) => *v,
|
||||
None => {
|
||||
return Err(OperationError::InvalidEntryState);
|
||||
}
|
||||
};
|
||||
let uuid = e
|
||||
.get_ava_single_uuid("uuid")
|
||||
.ok_or(OperationError::InvalidEntryState)?;
|
||||
// Get the value and uuid
|
||||
//for each value in the ava.
|
||||
e.get_ava_set(attr)
|
||||
|
|
|
@ -86,7 +86,6 @@ impl Plugin for Base {
|
|||
for entry in cand.iter() {
|
||||
let uuid_ref: Uuid = entry
|
||||
.get_ava_single_uuid("uuid")
|
||||
.copied()
|
||||
.ok_or_else(|| OperationError::InvalidAttribute("uuid".to_string()))?;
|
||||
trace!("Entry valid UUID: {:?}", entry);
|
||||
if !cand_uuid.insert(uuid_ref) {
|
||||
|
@ -211,7 +210,7 @@ impl Plugin for Base {
|
|||
// uniqueness!
|
||||
let uuid = e.get_uuid();
|
||||
|
||||
if uuid_seen.insert(*uuid) {
|
||||
if uuid_seen.insert(uuid) {
|
||||
// Insert returns true if the item was unique.
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,7 @@ use tracing::trace;
|
|||
|
||||
lazy_static! {
|
||||
static ref PVCLASS_DOMAIN_INFO: PartialValue = PartialValue::new_class("domain_info");
|
||||
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuidr(&UUID_DOMAIN_INFO);
|
||||
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO);
|
||||
}
|
||||
|
||||
pub struct Domain {}
|
||||
|
|
|
@ -15,8 +15,8 @@ use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
|||
use crate::plugins::Plugin;
|
||||
use crate::prelude::*;
|
||||
use crate::value::{PartialValue, Value};
|
||||
use crate::valueset::ValueSet;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
@ -54,7 +54,7 @@ fn do_memberof(
|
|||
// Add all the direct mo's and mos.
|
||||
let r: Result<(), _> = groups.iter().try_for_each(|g| {
|
||||
// TODO: Change add_ava to remove this alloc/clone.
|
||||
let dmo = Value::new_refer(*g.get_uuid());
|
||||
let dmo = Value::new_refer(g.get_uuid());
|
||||
tgte.add_ava("directmemberof", dmo.clone());
|
||||
tgte.add_ava("memberof", dmo);
|
||||
|
||||
|
@ -117,7 +117,7 @@ fn apply_memberof(
|
|||
// Load the vecdeque with this batch.
|
||||
|
||||
while let Some((pre, mut tgte)) = work_set.pop() {
|
||||
let guuid = *pre.get_uuid();
|
||||
let guuid = pre.get_uuid();
|
||||
// load the entry from the db.
|
||||
if !tgte.attribute_equality("class", &CLASS_GROUP) {
|
||||
// It's not a group, we'll deal with you later. We should NOT
|
||||
|
@ -218,7 +218,6 @@ impl Plugin for MemberOf {
|
|||
})
|
||||
.flatten(),
|
||||
)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
apply_memberof(qs, group_affect)
|
||||
|
@ -257,7 +256,6 @@ impl Plugin for MemberOf {
|
|||
})
|
||||
.flatten(),
|
||||
)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
apply_memberof(qs, group_affect)
|
||||
|
@ -300,7 +298,6 @@ impl Plugin for MemberOf {
|
|||
}
|
||||
})
|
||||
.flatten()
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
apply_memberof(qs, group_affect)
|
||||
|
@ -323,7 +320,7 @@ impl Plugin for MemberOf {
|
|||
for e in all_cand {
|
||||
let filt_in = filter!(f_and!([
|
||||
f_eq("class", PartialValue::new_class("group")),
|
||||
f_eq("member", PartialValue::new_refer(*e.get_uuid()))
|
||||
f_eq("member", PartialValue::new_refer(e.get_uuid()))
|
||||
]));
|
||||
|
||||
let direct_memberof = match qs
|
||||
|
@ -335,19 +332,23 @@ impl Plugin for MemberOf {
|
|||
};
|
||||
// for all direct -> add uuid to map
|
||||
|
||||
let d_groups_set: Option<ValueSet> = direct_memberof
|
||||
.iter()
|
||||
.map(|e| Value::new_refer(*e.get_uuid()))
|
||||
.collect();
|
||||
let d_groups_set: BTreeSet<Uuid> =
|
||||
direct_memberof.iter().map(|e| e.get_uuid()).collect();
|
||||
|
||||
let d_groups_set = if d_groups_set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(d_groups_set)
|
||||
};
|
||||
|
||||
trace!("DMO search groups {:?} -> {:?}", e.get_uuid(), d_groups_set);
|
||||
|
||||
match (e.get_ava_set("directmemberof"), d_groups_set) {
|
||||
(Some(edmos), Some(dmos)) => {
|
||||
(Some(edmos), Some(b)) => {
|
||||
// Can they both be reference sets?
|
||||
match (edmos.as_refer_set(), dmos.as_refer_set()) {
|
||||
(Some(a), Some(b)) => {
|
||||
let diff: Vec<_> = a.symmetric_difference(b).collect();
|
||||
match edmos.as_refer_set() {
|
||||
Some(a) => {
|
||||
let diff: Vec<_> = a.symmetric_difference(&b).collect();
|
||||
if !diff.is_empty() {
|
||||
admin_error!(
|
||||
"MemberOfInvalid: Entry {}, DMO has inconsistencies -> {:?}",
|
||||
|
|
|
@ -162,7 +162,7 @@ mod tests {
|
|||
assert!(e.attribute_pres("oauth2_rs_basic_secret"));
|
||||
assert!(e.attribute_pres("oauth2_rs_token_key"));
|
||||
// Check the values are different.
|
||||
assert!(e.get_ava_single_str("oauth2_rs_basic_secret") != Some("12345"));
|
||||
assert!(e.get_ava_single_utf8("oauth2_rs_basic_secret") != Some("12345"));
|
||||
assert!(e.get_ava_single_secret("oauth2_rs_token_key") != Some("12345"));
|
||||
}
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ impl Plugin for PasswordImport {
|
|||
return Err(OperationError::Plugin(PluginError::PasswordImport("multiple password_imports specified".to_string())))
|
||||
}
|
||||
|
||||
let im_pw = vs.to_str_single()
|
||||
let im_pw = vs.to_utf8_single()
|
||||
.ok_or_else(|| OperationError::Plugin(PluginError::PasswordImport("password_import has incorrect value type".to_string())))?;
|
||||
|
||||
// convert the import_password to a cred
|
||||
|
@ -75,7 +75,7 @@ impl Plugin for PasswordImport {
|
|||
)));
|
||||
}
|
||||
|
||||
let im_pw = vs.to_str_single().ok_or_else(|| {
|
||||
let im_pw = vs.to_utf8_single().ok_or_else(|| {
|
||||
OperationError::Plugin(PluginError::PasswordImport(
|
||||
"password_import has incorrect value type".to_string(),
|
||||
))
|
||||
|
|
|
@ -106,7 +106,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
vsiter.try_for_each(|vs| {
|
||||
if let Some(uuid_iter) = vs.as_ref_uuid_iter() {
|
||||
uuid_iter.for_each(|u| {
|
||||
i.push(PartialValue::new_uuid(*u))
|
||||
i.push(PartialValue::new_uuid(u))
|
||||
});
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -185,7 +185,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
.map(|e| e.get_uuid())
|
||||
.map(|u| ref_types.values().map(move |r_type| {
|
||||
// For everything that references the uuid's in the deleted set.
|
||||
f_eq(r_type.name.as_str(), PartialValue::new_refer(*u))
|
||||
f_eq(r_type.name.as_str(), PartialValue::new_refer(u))
|
||||
}))
|
||||
.flatten()
|
||||
.collect(),
|
||||
|
@ -195,7 +195,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
|
||||
let removed_ids: BTreeSet<_> = cand
|
||||
.iter()
|
||||
.map(|e| PartialValue::new_refer(*e.get_uuid()))
|
||||
.map(|e| PartialValue::new_refer(e.get_uuid()))
|
||||
.collect();
|
||||
|
||||
let work_set = qs.internal_search_writeable(&filt)?;
|
||||
|
@ -226,7 +226,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
Err(e) => return vec![e],
|
||||
};
|
||||
|
||||
let acu_map: Set<&Uuid> = all_cand.iter().map(|e| e.get_uuid()).collect();
|
||||
let acu_map: Set<Uuid> = all_cand.iter().map(|e| e.get_uuid()).collect();
|
||||
|
||||
let schema = qs.get_schema();
|
||||
let ref_types = schema.get_reference_types();
|
||||
|
@ -242,7 +242,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
match vs.as_ref_uuid_iter() {
|
||||
Some(uuid_iter) => {
|
||||
for vu in uuid_iter {
|
||||
if acu_map.get(vu).is_none() {
|
||||
if acu_map.get(&vu).is_none() {
|
||||
res.push(Err(ConsistencyError::RefintNotUpheld(c.get_id())))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct Spn {}
|
|||
lazy_static! {
|
||||
static ref CLASS_GROUP: PartialValue = PartialValue::new_class("group");
|
||||
static ref CLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");
|
||||
static ref PV_UUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuidr(&UUID_DOMAIN_INFO);
|
||||
static ref PV_UUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO);
|
||||
}
|
||||
|
||||
impl Plugin for Spn {
|
||||
|
|
|
@ -104,7 +104,7 @@ impl SchemaAttribute {
|
|||
trace!("Converting -> {:?}", value);
|
||||
|
||||
// uuid
|
||||
let uuid = *value.get_uuid();
|
||||
let uuid = value.get_uuid();
|
||||
|
||||
// class
|
||||
if !value.attribute_equality("class", &PVCLASS_ATTRIBUTETYPE) {
|
||||
|
@ -116,7 +116,7 @@ impl SchemaAttribute {
|
|||
|
||||
// name
|
||||
let name = value
|
||||
.get_ava_single_str("attributename")
|
||||
.get_ava_single_iutf8("attributename")
|
||||
.map(|s| s.into())
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing attributename - {:?}", uuid);
|
||||
|
@ -124,7 +124,7 @@ impl SchemaAttribute {
|
|||
})?;
|
||||
// description
|
||||
let description = value
|
||||
.get_ava_single_str("description")
|
||||
.get_ava_single_utf8("description")
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing description - {}", name);
|
||||
|
@ -149,13 +149,10 @@ impl SchemaAttribute {
|
|||
OperationError::InvalidSchemaState("invalid index".to_string())
|
||||
})?;
|
||||
// syntax type
|
||||
let syntax = value
|
||||
.get_ava_single_syntax("syntax")
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing syntax - {}", name);
|
||||
OperationError::InvalidSchemaState("missing syntax".to_string())
|
||||
})?;
|
||||
let syntax = value.get_ava_single_syntax("syntax").ok_or_else(|| {
|
||||
admin_error!("missing syntax - {}", name);
|
||||
OperationError::InvalidSchemaState("missing syntax".to_string())
|
||||
})?;
|
||||
|
||||
Ok(SchemaAttribute {
|
||||
name,
|
||||
|
@ -260,35 +257,15 @@ impl SchemaAttribute {
|
|||
return Err(SchemaError::InvalidAttributeSyntax(a.to_string()));
|
||||
};
|
||||
// If syntax, check the type is correct
|
||||
let valid = match self.syntax {
|
||||
SyntaxType::Boolean => ava.is_bool(),
|
||||
SyntaxType::SYNTAX_ID => ava.is_syntax(),
|
||||
SyntaxType::Uuid => ava.is_uuid(),
|
||||
SyntaxType::REFERENCE_UUID => ava.is_refer(),
|
||||
SyntaxType::INDEX_ID => ava.is_index(),
|
||||
SyntaxType::Utf8StringInsensitive => ava.is_insensitive_utf8(),
|
||||
SyntaxType::Utf8StringIname => ava.is_iname(),
|
||||
SyntaxType::UTF8STRING => ava.is_utf8(),
|
||||
SyntaxType::JSON_FILTER => ava.is_json_filter(),
|
||||
SyntaxType::Credential => ava.is_credential(),
|
||||
SyntaxType::SecretUtf8String => ava.is_secret_string(),
|
||||
SyntaxType::SshKey => ava.is_sshkey(),
|
||||
SyntaxType::SecurityPrincipalName => ava.is_spn(),
|
||||
SyntaxType::UINT32 => ava.is_uint32(),
|
||||
SyntaxType::Cid => ava.is_cid(),
|
||||
SyntaxType::NsUniqueId => ava.is_nsuniqueid(),
|
||||
SyntaxType::DateTime => ava.is_datetime(),
|
||||
SyntaxType::EmailAddress => ava.is_email_address(),
|
||||
SyntaxType::Url => ava.is_url(),
|
||||
SyntaxType::OauthScope => ava.is_oauthscope(),
|
||||
SyntaxType::OauthScopeMap => ava.is_oauthscopemap(),
|
||||
SyntaxType::PrivateBinary => ava.is_privatebinary(),
|
||||
SyntaxType::IntentToken => ava.is_intenttoken(),
|
||||
};
|
||||
if valid && ava.validate() {
|
||||
let valid = self.syntax == ava.syntax();
|
||||
if valid && ava.validate(self) {
|
||||
Ok(())
|
||||
} else {
|
||||
admin_error!(?a, "validate_ava - InvalidAttributeSyntax");
|
||||
admin_error!(
|
||||
?a,
|
||||
"validate_ava - InvalidAttributeSyntax for {:?}",
|
||||
self.syntax
|
||||
);
|
||||
Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +306,7 @@ impl SchemaClass {
|
|||
pub fn try_from(value: &Entry<EntrySealed, EntryCommitted>) -> Result<Self, OperationError> {
|
||||
trace!("Converting {:?}", value);
|
||||
// uuid
|
||||
let uuid = *value.get_uuid();
|
||||
let uuid = value.get_uuid();
|
||||
// Convert entry to a schema class.
|
||||
if !value.attribute_equality("class", &PVCLASS_CLASSTYPE) {
|
||||
admin_error!("class classtype not present - {:?}", uuid);
|
||||
|
@ -340,7 +317,7 @@ impl SchemaClass {
|
|||
|
||||
// name
|
||||
let name = value
|
||||
.get_ava_single_str("classname")
|
||||
.get_ava_single_iutf8("classname")
|
||||
.map(AttrString::from)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing classname - {:?}", uuid);
|
||||
|
@ -348,7 +325,7 @@ impl SchemaClass {
|
|||
})?;
|
||||
// description
|
||||
let description = value
|
||||
.get_ava_single_str("description")
|
||||
.get_ava_single_utf8("description")
|
||||
.map(String::from)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing description - {}", name);
|
||||
|
@ -357,19 +334,19 @@ impl SchemaClass {
|
|||
|
||||
// These are all "optional" lists of strings.
|
||||
let systemmay = value
|
||||
.get_ava_as_str("systemmay")
|
||||
.get_ava_iter_iutf8("systemmay")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let systemmust = value
|
||||
.get_ava_as_str("systemmust")
|
||||
.get_ava_iter_iutf8("systemmust")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let may = value
|
||||
.get_ava_as_str("may")
|
||||
.get_ava_iter_iutf8("may")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let must = value
|
||||
.get_ava_as_str("must")
|
||||
.get_ava_iter_iutf8("must")
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -1752,11 +1729,10 @@ mod tests {
|
|||
syntax: SyntaxType::Utf8StringInsensitive,
|
||||
};
|
||||
|
||||
let r1 =
|
||||
single_value_string.validate_ava("single_value", &valueset![Value::new_iutf8("test")]);
|
||||
let r1 = single_value_string.validate_ava("single_value", &(vs_iutf8!["test"] as _));
|
||||
assert_eq!(r1, Ok(()));
|
||||
|
||||
let rvs = unsafe { valueset![Value::new_iutf8("test1"), Value::new_iutf8("test2")] };
|
||||
let rvs = vs_iutf8!["test1", "test2"] as _;
|
||||
let r2 = single_value_string.validate_ava("single_value", &rvs);
|
||||
assert_eq!(
|
||||
r2,
|
||||
|
@ -1779,7 +1755,7 @@ mod tests {
|
|||
syntax: SyntaxType::UTF8STRING,
|
||||
};
|
||||
|
||||
let rvs = unsafe { valueset![Value::new_utf8s("test1"), Value::new_utf8s("test2")] };
|
||||
let rvs = vs_utf8!["test1".to_string(), "test2".to_string()] as _;
|
||||
let r5 = multi_value_string.validate_ava("mv_string", &rvs);
|
||||
assert_eq!(r5, Ok(()));
|
||||
|
||||
|
@ -1811,8 +1787,8 @@ mod tests {
|
|||
);
|
||||
*/
|
||||
|
||||
let rvs = unsafe { valueset![Value::new_bool(true), Value::new_bool(false)] };
|
||||
let r4 = multi_value_boolean.validate_ava("mv_bool", &rvs);
|
||||
let rvs = vs_bool![true, false];
|
||||
let r4 = multi_value_boolean.validate_ava("mv_bool", &(rvs as _));
|
||||
assert_eq!(r4, Ok(()));
|
||||
|
||||
// syntax_id and index_type values
|
||||
|
@ -1828,14 +1804,12 @@ mod tests {
|
|||
syntax: SyntaxType::SYNTAX_ID,
|
||||
};
|
||||
|
||||
let rvs = ValueSet::new(Value::new_syntaxs("UTF8STRING").unwrap());
|
||||
let rvs = vs_syntax![SyntaxType::try_from("UTF8STRING").unwrap()] as _;
|
||||
let r6 = single_value_syntax.validate_ava("sv_syntax", &rvs);
|
||||
assert_eq!(r6, Ok(()));
|
||||
|
||||
let r7 = single_value_syntax.validate_ava(
|
||||
"sv_syntax",
|
||||
&ValueSet::new(Value::new_utf8s("thaeountaheu")),
|
||||
);
|
||||
let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
|
||||
let r7 = single_value_syntax.validate_ava("sv_syntax", &rvs);
|
||||
assert_eq!(
|
||||
r7,
|
||||
Err(SchemaError::InvalidAttributeSyntax("sv_syntax".to_string()))
|
||||
|
@ -1853,14 +1827,12 @@ mod tests {
|
|||
syntax: SyntaxType::INDEX_ID,
|
||||
};
|
||||
//
|
||||
let r8 = single_value_index.validate_ava(
|
||||
"sv_index",
|
||||
&ValueSet::new(Value::new_indexs("EQUALITY").unwrap()),
|
||||
);
|
||||
let rvs = vs_index![IndexType::try_from("EQUALITY").unwrap()] as _;
|
||||
let r8 = single_value_index.validate_ava("sv_index", &rvs);
|
||||
assert_eq!(r8, Ok(()));
|
||||
|
||||
let r9 = single_value_index
|
||||
.validate_ava("sv_index", &ValueSet::new(Value::new_utf8s("thaeountaheu")));
|
||||
let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
|
||||
let r9 = single_value_index.validate_ava("sv_index", &rvs);
|
||||
assert_eq!(
|
||||
r9,
|
||||
Err(SchemaError::InvalidAttributeSyntax("sv_index".to_string()))
|
||||
|
|
|
@ -33,6 +33,7 @@ use crate::schema::{
|
|||
Schema, SchemaAttribute, SchemaClass, SchemaReadTransaction, SchemaTransaction,
|
||||
SchemaWriteTransaction,
|
||||
};
|
||||
use crate::valueset::uuid_to_proto_string;
|
||||
use kanidm_proto::v1::{ConsistencyError, SchemaError};
|
||||
|
||||
const RESOLVE_FILTER_CACHE_MAX: usize = 4096;
|
||||
|
@ -49,7 +50,7 @@ lazy_static! {
|
|||
static ref PVCLASS_ACC: PartialValue = PartialValue::new_class("access_control_create");
|
||||
static ref PVCLASS_ACP: PartialValue = PartialValue::new_class("access_control_profile");
|
||||
static ref PVCLASS_OAUTH2_RS: PartialValue = PartialValue::new_class("oauth2_resource_server");
|
||||
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuidr(&UUID_DOMAIN_INFO);
|
||||
static ref PVUUID_DOMAIN_INFO: PartialValue = PartialValue::new_uuid(*UUID_DOMAIN_INFO);
|
||||
static ref PVACP_ENABLE_FALSE: PartialValue = PartialValue::new_bool(false);
|
||||
}
|
||||
|
||||
|
@ -286,7 +287,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn uuid_to_spn(&self, uuid: &Uuid) -> Result<Option<Value>, OperationError> {
|
||||
fn uuid_to_spn(&self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
|
||||
let r = self.get_be_txn().uuid2spn(uuid)?;
|
||||
|
||||
if let Some(ref n) = r {
|
||||
|
@ -298,7 +299,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
Ok(r)
|
||||
}
|
||||
|
||||
fn uuid_to_rdn(&self, uuid: &Uuid) -> Result<String, OperationError> {
|
||||
fn uuid_to_rdn(&self, uuid: Uuid) -> Result<String, OperationError> {
|
||||
// If we have a some, pass it on, else unwrap into a default.
|
||||
self.get_be_txn()
|
||||
.uuid2rdn(uuid)
|
||||
|
@ -661,11 +662,12 @@ pub trait QueryServerTransaction<'a> {
|
|||
if let Some(r_set) = value.as_refer_set() {
|
||||
let v: Result<Vec<_>, _> = r_set
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|ur| {
|
||||
let nv = self.uuid_to_spn(ur)?;
|
||||
match nv {
|
||||
Some(v) => Ok(v.to_proto_string_clone()),
|
||||
None => Ok(ValueSet::uuid_to_proto_string(ur)),
|
||||
None => Ok(uuid_to_proto_string(ur)),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
@ -674,10 +676,10 @@ pub trait QueryServerTransaction<'a> {
|
|||
let v: Result<Vec<_>, _> = r_map
|
||||
.iter()
|
||||
.map(|(u, m)| {
|
||||
let nv = self.uuid_to_spn(u)?;
|
||||
let nv = self.uuid_to_spn(*u)?;
|
||||
let u = match nv {
|
||||
Some(v) => v.to_proto_string_clone(),
|
||||
None => ValueSet::uuid_to_proto_string(u),
|
||||
None => uuid_to_proto_string(*u),
|
||||
};
|
||||
Ok(format!("{}: {:?}", u, m))
|
||||
})
|
||||
|
@ -697,6 +699,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
if let Some(r_set) = value.as_refer_set() {
|
||||
let v: Result<Vec<_>, _> = r_set
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|ur| {
|
||||
let rdn = self.uuid_to_rdn(ur)?;
|
||||
Ok(format!("{},{}", rdn, basedn))
|
||||
|
@ -715,7 +718,8 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_db_domain_name(&self) -> Result<String, OperationError> {
|
||||
self.internal_search_uuid(&UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
e.get_ava_single_str("domain_name")
|
||||
trace!(?e);
|
||||
e.get_ava_single_iname("domain_name")
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidEntryState)
|
||||
})
|
||||
|
@ -754,7 +758,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
// This is a helper to get password badlist.
|
||||
fn get_password_badlist(&self) -> Result<HashSet<String>, OperationError> {
|
||||
self.internal_search_uuid(&UUID_SYSTEM_CONFIG)
|
||||
.and_then(|e| match e.get_ava_as_str("badlist_password") {
|
||||
.and_then(|e| match e.get_ava_iter_iutf8("badlist_password") {
|
||||
Some(vs_str_iter) => {
|
||||
let badlist_hashset: HashSet<_> = vs_str_iter.map(str::to_string).collect();
|
||||
Ok(badlist_hashset)
|
||||
|
@ -998,7 +1002,7 @@ impl QueryServer {
|
|||
let d_info = self.d_info.write();
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let ts_max = be_txn.get_db_ts_max(&ts).expect("Unable to get db_ts_max");
|
||||
let ts_max = be_txn.get_db_ts_max(ts).expect("Unable to get db_ts_max");
|
||||
let cid = Cid::new_lamport(self.s_uuid, d_info.d_uuid, ts, &ts_max);
|
||||
|
||||
QueryServerWriteTransaction {
|
||||
|
@ -1515,12 +1519,12 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
|
||||
for e in revive_cands {
|
||||
// Get this entries uuid.
|
||||
let u: Uuid = *e.get_uuid();
|
||||
let u: Uuid = e.get_uuid();
|
||||
|
||||
if let Some(riter) = e.get_ava_as_refuuid("directmemberof") {
|
||||
for g_uuid in riter {
|
||||
dm_mods
|
||||
.entry(*g_uuid)
|
||||
.entry(g_uuid)
|
||||
.and_modify(|mlist| {
|
||||
let m = Modify::Present(
|
||||
AttrString::from("member"),
|
||||
|
@ -1611,6 +1615,9 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
trace!("modify: pre_candidates -> {:?}", pre_candidates);
|
||||
trace!("modify: modlist -> {:?}", me.modlist);
|
||||
|
||||
// Are we allowed to make the changes we want to?
|
||||
// modify_allow_operation
|
||||
let access = self.get_accesscontrols();
|
||||
|
@ -1940,10 +1947,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
|
||||
candidates.iter_mut().try_for_each(|er| {
|
||||
if let Some(vs) = er.get_ava_mut("name") {
|
||||
vs.migrate_iutf8_iname()?
|
||||
if let Some(mut nvs) = vs.migrate_iutf8_iname()? {
|
||||
std::mem::swap(&mut nvs, vs)
|
||||
}
|
||||
};
|
||||
if let Some(vs) = er.get_ava_mut("domain_name") {
|
||||
vs.migrate_iutf8_iname()?
|
||||
if let Some(mut nvs) = vs.migrate_iutf8_iname()? {
|
||||
std::mem::swap(&mut nvs, vs)
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -2656,21 +2667,23 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
fn reload_domain_info(&mut self) -> Result<(), OperationError> {
|
||||
let domain_name = self.get_db_domain_name()?;
|
||||
spanned!("server::reload_domain_info", {
|
||||
let domain_name = self.get_db_domain_name()?;
|
||||
|
||||
let mut_d_info = self.d_info.get_mut();
|
||||
if mut_d_info.d_name != domain_name {
|
||||
admin_warn!(
|
||||
"Using database configured domain name {} - was {}",
|
||||
domain_name,
|
||||
mut_d_info.d_name,
|
||||
);
|
||||
admin_warn!(
|
||||
"If you think this is an error, see https://kanidm.github.io/kanidm/administrivia.html#rename-the-domain"
|
||||
);
|
||||
mut_d_info.d_name = domain_name;
|
||||
}
|
||||
Ok(())
|
||||
let mut_d_info = self.d_info.get_mut();
|
||||
if mut_d_info.d_name != domain_name {
|
||||
admin_warn!(
|
||||
"Using database configured domain name {} - was {}",
|
||||
domain_name,
|
||||
mut_d_info.d_name,
|
||||
);
|
||||
admin_warn!(
|
||||
"If you think this is an error, see https://kanidm.github.io/kanidm/administrivia.html#rename-the-domain"
|
||||
);
|
||||
mut_d_info.d_name = domain_name;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Initiate a domain rename process. This is generally an internal function but it's
|
||||
|
@ -2693,7 +2706,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
new_domain_name: &str,
|
||||
) -> Result<(), OperationError> {
|
||||
let modl = ModifyList::new_purge_and_set("domain_name", Value::new_iname(new_domain_name));
|
||||
let udi = PartialValue::new_uuidr(&UUID_DOMAIN_INFO);
|
||||
let udi = PVUUID_DOMAIN_INFO.clone();
|
||||
let filt = filter_all!(f_eq("uuid", udi));
|
||||
self.internal_modify(&filt, &modl)
|
||||
}
|
||||
|
@ -2764,7 +2777,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
|
||||
// Write the cid to the db. If this fails, we can't assume replication
|
||||
// will be stable, so return if it fails.
|
||||
be_txn.set_db_ts_max(&cid.ts)?;
|
||||
be_txn.set_db_ts_max(cid.ts)?;
|
||||
// Validate the schema as we just loaded it.
|
||||
let r = schema.validate();
|
||||
|
||||
|
@ -3488,17 +3501,17 @@ mod tests {
|
|||
|
||||
// Name doesn't exist
|
||||
let r1 = server_txn
|
||||
.uuid_to_spn(&Uuid::parse_str("bae3f507-e6c3-44ba-ad01-f8ff1083534a").unwrap());
|
||||
.uuid_to_spn(Uuid::parse_str("bae3f507-e6c3-44ba-ad01-f8ff1083534a").unwrap());
|
||||
// There is nothing.
|
||||
assert!(r1 == Ok(None));
|
||||
// Name does exist
|
||||
let r3 = server_txn
|
||||
.uuid_to_spn(&Uuid::parse_str("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap());
|
||||
.uuid_to_spn(Uuid::parse_str("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap());
|
||||
println!("{:?}", r3);
|
||||
assert!(r3.unwrap().unwrap() == Value::new_spn_str("testperson1", "example.com"));
|
||||
// Name is not syntax normalised (but exists)
|
||||
let r4 = server_txn
|
||||
.uuid_to_spn(&Uuid::parse_str("CC8E95B4-C24F-4D68-BA54-8BED76F63930").unwrap());
|
||||
.uuid_to_spn(Uuid::parse_str("CC8E95B4-C24F-4D68-BA54-8BED76F63930").unwrap());
|
||||
assert!(r4.unwrap().unwrap() == Value::new_spn_str("testperson1", "example.com"));
|
||||
})
|
||||
}
|
||||
|
@ -3526,17 +3539,17 @@ mod tests {
|
|||
|
||||
// Name doesn't exist
|
||||
let r1 = server_txn
|
||||
.uuid_to_rdn(&Uuid::parse_str("bae3f507-e6c3-44ba-ad01-f8ff1083534a").unwrap());
|
||||
.uuid_to_rdn(Uuid::parse_str("bae3f507-e6c3-44ba-ad01-f8ff1083534a").unwrap());
|
||||
// There is nothing.
|
||||
assert!(r1.unwrap() == "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
|
||||
// Name does exist
|
||||
let r3 = server_txn
|
||||
.uuid_to_rdn(&Uuid::parse_str("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap());
|
||||
.uuid_to_rdn(Uuid::parse_str("cc8e95b4-c24f-4d68-ba54-8bed76f63930").unwrap());
|
||||
println!("{:?}", r3);
|
||||
assert!(r3.unwrap() == "spn=testperson1@example.com");
|
||||
// Uuid is not syntax normalised (but exists)
|
||||
let r4 = server_txn
|
||||
.uuid_to_rdn(&Uuid::parse_str("CC8E95B4-C24F-4D68-BA54-8BED76F63930").unwrap());
|
||||
.uuid_to_rdn(Uuid::parse_str("CC8E95B4-C24F-4D68-BA54-8BED76F63930").unwrap());
|
||||
assert!(r4.unwrap() == "spn=testperson1@example.com");
|
||||
})
|
||||
}
|
||||
|
@ -3565,12 +3578,10 @@ mod tests {
|
|||
let cr = server_txn.create(&ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
assert!(
|
||||
server_txn.uuid_to_rdn(&tuuid) == Ok("spn=testperson1@example.com".to_string())
|
||||
);
|
||||
assert!(server_txn.uuid_to_rdn(tuuid) == Ok("spn=testperson1@example.com".to_string()));
|
||||
|
||||
assert!(
|
||||
server_txn.uuid_to_spn(&tuuid)
|
||||
server_txn.uuid_to_spn(tuuid)
|
||||
== Ok(Some(Value::new_spn_str("testperson1", "example.com")))
|
||||
);
|
||||
|
||||
|
@ -3587,11 +3598,11 @@ mod tests {
|
|||
|
||||
// all should fail
|
||||
assert!(
|
||||
server_txn.uuid_to_rdn(&tuuid)
|
||||
server_txn.uuid_to_rdn(tuuid)
|
||||
== Ok("uuid=cc8e95b4-c24f-4d68-ba54-8bed76f63930".to_string())
|
||||
);
|
||||
|
||||
assert!(server_txn.uuid_to_spn(&tuuid) == Ok(None));
|
||||
assert!(server_txn.uuid_to_spn(tuuid) == Ok(None));
|
||||
|
||||
assert!(server_txn.name_to_uuid("testperson1").is_err());
|
||||
|
||||
|
@ -3609,12 +3620,10 @@ mod tests {
|
|||
|
||||
// all checks pass
|
||||
|
||||
assert!(
|
||||
server_txn.uuid_to_rdn(&tuuid) == Ok("spn=testperson1@example.com".to_string())
|
||||
);
|
||||
assert!(server_txn.uuid_to_rdn(tuuid) == Ok("spn=testperson1@example.com".to_string()));
|
||||
|
||||
assert!(
|
||||
server_txn.uuid_to_spn(&tuuid)
|
||||
server_txn.uuid_to_spn(tuuid)
|
||||
== Ok(Some(Value::new_spn_str("testperson1", "example.com")))
|
||||
);
|
||||
|
||||
|
@ -4131,11 +4140,11 @@ mod tests {
|
|||
assert!(server_txn.modify(&me_syn).is_ok());
|
||||
assert!(server_txn.commit().is_ok());
|
||||
|
||||
let server_txn = server.write(duration_from_epoch_now());
|
||||
let mut server_txn = server.write(duration_from_epoch_now());
|
||||
// ++ Mod domain name and name to be the old type.
|
||||
let me_dn = unsafe {
|
||||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq("uuid", PartialValue::new_uuidr(&UUID_DOMAIN_INFO))),
|
||||
filter!(f_eq("uuid", PartialValue::new_uuid(*UUID_DOMAIN_INFO))),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Purged(AttrString::from("name")),
|
||||
Modify::Purged(AttrString::from("domain_name")),
|
||||
|
@ -4148,13 +4157,17 @@ mod tests {
|
|||
)
|
||||
};
|
||||
assert!(server_txn.modify(&me_dn).is_ok());
|
||||
|
||||
// Now, both the types are invalid.
|
||||
assert!(server_txn.commit().is_ok());
|
||||
|
||||
// WARNING! We can't commit here because this triggers domain_reload which will fail
|
||||
// due to incorrect syntax of the domain name! Run the migration in the same txn!
|
||||
// Trigger a schema reload.
|
||||
assert!(server_txn.reload_schema().is_ok());
|
||||
|
||||
// We can't just re-run the migrate here because name takes it's definition from
|
||||
// in memory, and we can't re-run the initial memory gen. So we just fix it to match
|
||||
// what the migrate "would do".
|
||||
let server_txn = server.write(duration_from_epoch_now());
|
||||
let me_syn = unsafe {
|
||||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_or!([
|
||||
|
@ -4168,11 +4181,15 @@ mod tests {
|
|||
)
|
||||
};
|
||||
assert!(server_txn.modify(&me_syn).is_ok());
|
||||
assert!(server_txn.commit().is_ok());
|
||||
|
||||
// WARNING! We can't commit here because this triggers domain_reload which will fail
|
||||
// due to incorrect syntax of the domain name! Run the migration in the same txn!
|
||||
// Trigger a schema reload.
|
||||
assert!(server_txn.reload_schema().is_ok());
|
||||
|
||||
// ++ Run the upgrade for X to Y
|
||||
let server_txn = server.write(duration_from_epoch_now());
|
||||
assert!(server_txn.migrate_2_to_3().is_ok());
|
||||
|
||||
assert!(server_txn.commit().is_ok());
|
||||
|
||||
// Assert that it migrated and worked as expected.
|
||||
|
@ -4181,12 +4198,18 @@ mod tests {
|
|||
.internal_search_uuid(&UUID_DOMAIN_INFO)
|
||||
.expect("failed");
|
||||
// ++ assert all names are iname
|
||||
assert!(domain.get_ava_set("name").expect("no name?").is_iname());
|
||||
assert!(
|
||||
domain.get_ava_set("name").expect("no name?").syntax()
|
||||
== SyntaxType::Utf8StringIname
|
||||
);
|
||||
// ++ assert all domain/domain_name are iname
|
||||
assert!(domain
|
||||
.get_ava_set("domain_name")
|
||||
.expect("no domain_name?")
|
||||
.is_iname());
|
||||
assert!(
|
||||
domain
|
||||
.get_ava_set("domain_name")
|
||||
.expect("no domain_name?")
|
||||
.syntax()
|
||||
== SyntaxType::Utf8StringIname
|
||||
);
|
||||
assert!(server_txn.commit().is_ok());
|
||||
})
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ pub struct DistinctAlpha;
|
|||
|
||||
pub type Sid = [u8; 4];
|
||||
|
||||
pub fn uuid_to_gid_u32(u: &Uuid) -> u32 {
|
||||
pub fn uuid_to_gid_u32(u: Uuid) -> u32 {
|
||||
let b_ref = u.as_bytes();
|
||||
let mut x: [u8; 4] = [0; 4];
|
||||
x.clone_from_slice(&b_ref[12..16]);
|
||||
|
@ -61,24 +61,26 @@ pub fn backup_code_from_random() -> HashSet<String> {
|
|||
}
|
||||
|
||||
pub fn readable_password_from_random() -> String {
|
||||
// 2^112 bits, means we need at least 55^20 to have as many bits of entropy.
|
||||
// this leads us to 4 groups of 5 to create 55^20
|
||||
let mut trng = thread_rng();
|
||||
format!(
|
||||
"{}-{}-{}-{}",
|
||||
(&mut trng)
|
||||
.sample_iter(&DistinctAlpha)
|
||||
.take(4)
|
||||
.take(5)
|
||||
.collect::<String>(),
|
||||
(&mut trng)
|
||||
.sample_iter(&DistinctAlpha)
|
||||
.take(4)
|
||||
.take(5)
|
||||
.collect::<String>(),
|
||||
(&mut trng)
|
||||
.sample_iter(&DistinctAlpha)
|
||||
.take(4)
|
||||
.take(5)
|
||||
.collect::<String>(),
|
||||
(&mut trng)
|
||||
.sample_iter(&DistinctAlpha)
|
||||
.take(4)
|
||||
.take(5)
|
||||
.collect::<String>(),
|
||||
)
|
||||
}
|
||||
|
@ -206,15 +208,15 @@ mod tests {
|
|||
#[test]
|
||||
fn test_utils_uuid_to_gid_u32() {
|
||||
let u1 = Uuid::parse_str("00000000-0000-0001-0000-000000000000").unwrap();
|
||||
let r1 = uuid_to_gid_u32(&u1);
|
||||
let r1 = uuid_to_gid_u32(u1);
|
||||
assert!(r1 == 0);
|
||||
|
||||
let u2 = Uuid::parse_str("00000000-0000-0001-0000-0000ffffffff").unwrap();
|
||||
let r2 = uuid_to_gid_u32(&u2);
|
||||
let r2 = uuid_to_gid_u32(u2);
|
||||
assert!(r2 == 0xffffffff);
|
||||
|
||||
let u3 = Uuid::parse_str("00000000-0000-0001-0000-ffff12345678").unwrap();
|
||||
let r3 = uuid_to_gid_u32(&u3);
|
||||
let r3 = uuid_to_gid_u32(u3);
|
||||
assert!(r3 == 0x12345678);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ lazy_static! {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialOrd, Ord, Eq, PartialEq, Hash)]
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#AddressClaim
|
||||
pub struct Address {
|
||||
pub formatted: String,
|
||||
|
@ -61,7 +61,7 @@ pub struct Address {
|
|||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Hash)]
|
||||
pub enum IndexType {
|
||||
Equality,
|
||||
Presence,
|
||||
|
@ -137,7 +137,7 @@ impl fmt::Display for IndexType {
|
|||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Hash, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
pub enum SyntaxType {
|
||||
UTF8STRING,
|
||||
Utf8StringInsensitive,
|
||||
|
@ -435,10 +435,6 @@ impl PartialValue {
|
|||
PartialValue::Uuid(u)
|
||||
}
|
||||
|
||||
pub fn new_uuidr(u: &Uuid) -> Self {
|
||||
PartialValue::Uuid(*u)
|
||||
}
|
||||
|
||||
pub fn new_uuids(us: &str) -> Option<Self> {
|
||||
Uuid::parse_str(us).map(PartialValue::Uuid).ok()
|
||||
}
|
||||
|
@ -483,7 +479,12 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn new_json_filter_s(s: &str) -> Option<Self> {
|
||||
serde_json::from_str(s).map(PartialValue::JsonFilt).ok()
|
||||
serde_json::from_str(s)
|
||||
.map(PartialValue::JsonFilt)
|
||||
.map_err(|e| {
|
||||
trace!(?e, ?s);
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn is_json_filter(&self) -> bool {
|
||||
|
@ -930,10 +931,6 @@ impl Value {
|
|||
Uuid::parse_str(s).map(Value::Uuid).ok()
|
||||
}
|
||||
|
||||
pub fn new_uuidr(u: &Uuid) -> Self {
|
||||
Value::Uuid(*u)
|
||||
}
|
||||
|
||||
// Is this correct? Should ref be seperate?
|
||||
pub fn is_uuid(&self) -> bool {
|
||||
matches!(self, Value::Uuid(_))
|
||||
|
|
File diff suppressed because it is too large
Load diff
449
kanidmd/idm/src/valueset/address.rs
Normal file
449
kanidmd/idm/src/valueset/address.rs
Normal file
|
@ -0,0 +1,449 @@
|
|||
use crate::be::dbvalue::DbValueAddressV1;
|
||||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::value::Address;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetAddress {
|
||||
set: SmolSet<[Address; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetAddress {
|
||||
pub fn new(b: Address) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetAddress { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: Address) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbValueAddressV1>) -> Result<ValueSet, OperationError> {
|
||||
let set = data
|
||||
.into_iter()
|
||||
.map(
|
||||
|DbValueAddressV1 {
|
||||
formatted,
|
||||
street_address,
|
||||
locality,
|
||||
region,
|
||||
postal_code,
|
||||
country,
|
||||
}| {
|
||||
Address {
|
||||
formatted,
|
||||
street_address,
|
||||
locality,
|
||||
region,
|
||||
postal_code,
|
||||
country,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
Ok(Box::new(ValueSetAddress { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Address>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetAddress { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetAddress {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Address(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Address(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Address(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
unreachable!();
|
||||
// self.set.iter().map(|b| b.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|a| a.formatted.clone()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Address(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|a| DbValueAddressV1 {
|
||||
formatted: a.formatted.clone(),
|
||||
street_address: a.street_address.clone(),
|
||||
locality: a.locality.clone(),
|
||||
region: a.region.clone(),
|
||||
postal_code: a.postal_code.clone(),
|
||||
country: a.country.clone(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|s| PartialValue::Address(s.formatted.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::Address))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_address_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_address_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn to_address_single(&self) -> Option<&Address> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn as_address_set(&self) -> Option<&SmolSet<[Address; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetEmailAddress {
|
||||
primary: String,
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetEmailAddress {
|
||||
pub fn new(primary: String) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(primary.clone());
|
||||
Box::new(ValueSetEmailAddress { primary, set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, a: String, primary: bool) -> bool {
|
||||
if primary {
|
||||
self.primary = a.clone();
|
||||
}
|
||||
self.set.insert(a)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(primary: String, data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set: BTreeSet<_> = data.into_iter().collect();
|
||||
|
||||
if set.contains(&primary) {
|
||||
Ok(Box::new(ValueSetEmailAddress { primary, set }))
|
||||
} else {
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (String, bool)>,
|
||||
{
|
||||
let mut primary = None;
|
||||
let set = iter
|
||||
.into_iter()
|
||||
.map(|(a, p)| {
|
||||
if p {
|
||||
primary = Some(a.clone());
|
||||
}
|
||||
a
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(primary) = primary {
|
||||
Some(Box::new(ValueSetEmailAddress { primary, set }))
|
||||
} else {
|
||||
if let Some(primary) = set.iter().cloned().take(1).next() {
|
||||
Some(Box::new(ValueSetEmailAddress { primary, set }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetEmailAddress {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::EmailAddress(a, p) => {
|
||||
// if the set was empty, we need to force update primary.
|
||||
if p || self.set.is_empty() {
|
||||
self.primary = a.clone();
|
||||
}
|
||||
Ok(self.set.insert(a))
|
||||
}
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::EmailAddress(a) => {
|
||||
let r = self.set.remove(a);
|
||||
if &self.primary == a {
|
||||
// if we can, inject another former address into primary.
|
||||
if let Some(n) = self.set.iter().take(1).cloned().next() {
|
||||
self.primary = n
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::EmailAddress(a) => self.set.contains(a),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::EmailAddress
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.set.contains(&self.primary)
|
||||
&& self
|
||||
.set
|
||||
.iter()
|
||||
.all(|mail| validator::validate_email(mail.as_str()))
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::EmailAddress(self.primary.clone(), self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::EmailAddress))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(|a| {
|
||||
let p = a == self.primary;
|
||||
Value::EmailAddress(a, p)
|
||||
}))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some((p_b, set_b)) = other.as_emailaddress_set() {
|
||||
&self.set == set_b && &self.primary == p_b
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some((_p, set_b)) = other.as_emailaddress_set() {
|
||||
mergesets!(self.set, set_b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_emailaddress_set(&self) -> Option<(&String, &BTreeSet<String>)> {
|
||||
if self.set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((&self.primary, &self.set))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_email_address_primary_str(&self) -> Option<&str> {
|
||||
if self.set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.primary.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn as_email_str_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetPhoneNumber {
|
||||
primary: String,
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ValueSetEmailAddress;
|
||||
use crate::value::{PartialValue, Value};
|
||||
use crate::valueset::{self, ValueSet};
|
||||
|
||||
#[test]
|
||||
fn test_valueset_emailaddress() {
|
||||
// Can be created
|
||||
//
|
||||
let mut vs: ValueSet = ValueSetEmailAddress::new("claire@example.com".to_string());
|
||||
|
||||
assert!(vs.len() == 1);
|
||||
assert!(vs.to_email_address_primary_str() == Some("claire@example.com"));
|
||||
|
||||
// Add another, still not primary.
|
||||
assert!(
|
||||
vs.insert_checked(
|
||||
Value::new_email_address_s("alice@example.com").expect("Invalid Email")
|
||||
) == Ok(true)
|
||||
);
|
||||
|
||||
assert!(vs.len() == 2);
|
||||
assert!(vs.to_email_address_primary_str() == Some("claire@example.com"));
|
||||
|
||||
// Update primary
|
||||
assert!(
|
||||
vs.insert_checked(
|
||||
Value::new_email_address_primary_s("primary@example.com").expect("Invalid Email")
|
||||
) == Ok(true)
|
||||
);
|
||||
assert!(vs.to_email_address_primary_str() == Some("primary@example.com"));
|
||||
|
||||
// Restore from dbv1, ensure correct primary
|
||||
let vs2 = valueset::from_db_valueset_v2(vs.to_db_valueset_v2())
|
||||
.expect("Failed to construct vs2 from dbvalue");
|
||||
|
||||
assert!(&vs == &vs2);
|
||||
assert!(vs.to_email_address_primary_str() == vs2.to_email_address_primary_str());
|
||||
|
||||
// Remove primary, assert it's gone and that the "first" address is assigned.
|
||||
assert!(vs.remove(&PartialValue::new_email_address_s("primary@example.com")));
|
||||
assert!(vs.len() == 2);
|
||||
assert!(vs.to_email_address_primary_str() == Some("alice@example.com"));
|
||||
|
||||
// Restore from dbv1, alice persisted.
|
||||
let vs3 = valueset::from_db_valueset_v2(vs.to_db_valueset_v2())
|
||||
.expect("Failed to construct vs2 from dbvalue");
|
||||
assert!(&vs == &vs3);
|
||||
assert!(vs3.len() == 2);
|
||||
assert!(vs3
|
||||
.as_emailaddress_set()
|
||||
.map(|(_p, s)| s)
|
||||
.unwrap()
|
||||
.contains("alice@example.com"));
|
||||
assert!(vs3
|
||||
.as_emailaddress_set()
|
||||
.map(|(_p, s)| s)
|
||||
.unwrap()
|
||||
.contains("claire@example.com"));
|
||||
|
||||
// If we clear, no primary.
|
||||
vs.clear();
|
||||
assert!(vs.len() == 0);
|
||||
assert!(vs.to_email_address_primary_str().is_none());
|
||||
}
|
||||
}
|
273
kanidmd/idm/src/valueset/binary.rs
Normal file
273
kanidmd/idm/src/valueset/binary.rs
Normal file
|
@ -0,0 +1,273 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
use std::collections::btree_map::Entry as BTreeEntry;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetPrivateBinary {
|
||||
set: SmolSet<[Vec<u8>; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetPrivateBinary {
|
||||
pub fn new(b: Vec<u8>) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetPrivateBinary { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: Vec<u8>) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<Vec<u8>>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetPrivateBinary { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Vec<u8>>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetPrivateBinary { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetPrivateBinary {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::PrivateBinary(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, _pv: &PartialValue) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn contains(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::PrivateBinary
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|_| "private_binary".to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::PrivateBinary(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|_| PartialValue::PrivateBinary),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::PrivateBinary))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_private_binary_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_private_binary_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_private_binary_single(&self) -> Option<&[u8]> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().map(|b| b.as_slice()).take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_private_binary_set(&self) -> Option<&SmolSet<[Vec<u8>; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetPublicBinary {
|
||||
map: BTreeMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl ValueSetPublicBinary {
|
||||
pub fn new(t: String, b: Vec<u8>) -> Box<Self> {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(t, b);
|
||||
Box::new(ValueSetPublicBinary { map })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, t: String, b: Vec<u8>) -> bool {
|
||||
self.map.insert(t, b).is_none()
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<(String, Vec<u8>)>) -> Result<ValueSet, OperationError> {
|
||||
let map = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetPublicBinary { map }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (String, Vec<u8>)>,
|
||||
{
|
||||
let map = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetPublicBinary { map }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetPublicBinary {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::PublicBinary(t, b) => {
|
||||
if let BTreeEntry::Vacant(e) = self.map.entry(t) {
|
||||
e.insert(b);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::PublicBinary(t) => self.map.remove(t.as_str()).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::PublicBinary(t) => self.map.contains_key(t.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.map.keys().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
unreachable!();
|
||||
// SyntaxType::PublicBinary
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.map.keys().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::PublicBinary(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(tag, bin)| (tag.clone(), bin.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.map.keys().cloned().map(PartialValue::PublicBinary))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(t, b)| Value::PublicBinary(t.clone(), b.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_publicbinary_map() {
|
||||
&self.map == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_publicbinary_map() {
|
||||
mergemaps!(self.map, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_publicbinary_map(&self) -> Option<&BTreeMap<String, Vec<u8>>> {
|
||||
Some(&self.map)
|
||||
}
|
||||
}
|
138
kanidmd/idm/src/valueset/bool.rs
Normal file
138
kanidmd/idm/src/valueset/bool.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetBool {
|
||||
set: SmolSet<[bool; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetBool {
|
||||
pub fn new(b: bool) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetBool { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: bool) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<bool>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetBool { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = bool>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetBool { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetBool {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Bool(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Bool(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Bool(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|b| b.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Boolean
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|b| b.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Bool(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|b| PartialValue::new_bool(b)))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|b| Value::new_bool(b)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_bool_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_bool_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bool_single(&self) -> Option<bool> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bool_set(&self) -> Option<&SmolSet<[bool; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
172
kanidmd/idm/src/valueset/cid.rs
Normal file
172
kanidmd/idm/src/valueset/cid.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
use crate::be::dbvalue::DbCidV1;
|
||||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetCid {
|
||||
set: SmolSet<[Cid; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetCid {
|
||||
pub fn new(u: Cid) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(u);
|
||||
Box::new(ValueSetCid { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, u: Cid) -> bool {
|
||||
self.set.insert(u)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbCidV1>) -> Result<ValueSet, OperationError> {
|
||||
let set = data
|
||||
.into_iter()
|
||||
.map(|dc| Cid {
|
||||
d_uuid: dc.domain_id,
|
||||
s_uuid: dc.server_id,
|
||||
ts: dc.timestamp,
|
||||
})
|
||||
.collect();
|
||||
Ok(Box::new(ValueSetCid { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Cid>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetCid { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetCid {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Cid(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Cid(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Cid(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Cid(c2) => self.set.iter().any(|c1| c1 < c2),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Cid
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|c| format!("{:?}_{}_{}", c.ts, c.d_uuid, c.s_uuid)),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Cid(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|c| DbCidV1 {
|
||||
domain_id: c.d_uuid,
|
||||
server_id: c.s_uuid,
|
||||
timestamp: c.ts,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(|u| PartialValue::new_cid(u)))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(|u| Value::new_cid(u)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_cid_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_cid_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn to_cid_single(&self) -> Option<Cid> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().cloned().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn as_cid_set(&self) -> Option<&SmolSet<[Cid; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
/*
|
||||
fn as_cid_iter(&self) -> Option<Box<dyn Iterator<Item = Cid> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
*/
|
||||
}
|
336
kanidmd/idm/src/valueset/cred.rs
Normal file
336
kanidmd/idm/src/valueset/cred.rs
Normal file
|
@ -0,0 +1,336 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::uuid_to_proto_string;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::btree_map::Entry as BTreeEntry;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::be::dbvalue::{DbValueCredV1, DbValueIntentTokenStateV1};
|
||||
use crate::credential::Credential;
|
||||
use crate::valueset::IntentTokenState;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetCredential {
|
||||
map: BTreeMap<String, Credential>,
|
||||
}
|
||||
|
||||
impl ValueSetCredential {
|
||||
pub fn new(t: String, c: Credential) -> Box<Self> {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(t, c);
|
||||
Box::new(ValueSetCredential { map })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, t: String, c: Credential) -> bool {
|
||||
self.map.insert(t, c).is_none()
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbValueCredV1>) -> Result<ValueSet, OperationError> {
|
||||
let map = data
|
||||
.into_iter()
|
||||
.map(|dc| {
|
||||
let t = dc.tag.clone();
|
||||
Credential::try_from(dc.data)
|
||||
.map_err(|()| OperationError::InvalidValueState)
|
||||
.map(|c| (t, c))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(Box::new(ValueSetCredential { map }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (String, Credential)>,
|
||||
{
|
||||
let map = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetCredential { map }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetCredential {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Cred(t, c) => {
|
||||
if let BTreeEntry::Vacant(e) = self.map.entry(t) {
|
||||
e.insert(c);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Cred(t) => self.map.remove(t.as_str()).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Cred(t) => self.map.contains_key(t.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.map.keys().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Credential
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.map.keys().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Credential(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(tag, cred)| DbValueCredV1 {
|
||||
tag: tag.clone(),
|
||||
data: cred.to_db_valuev1(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.map.keys().cloned().map(PartialValue::Cred))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(t, c)| Value::Cred(t.clone(), c.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, _other: &ValueSet) -> bool {
|
||||
// Looks like we may not need this?
|
||||
false
|
||||
/*
|
||||
if let Some(other) = other.as_credential_map() {
|
||||
&self.map == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_credential_map() {
|
||||
mergemaps!(self.map, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_credential_single(&self) -> Option<&Credential> {
|
||||
if self.map.len() == 1 {
|
||||
self.map.values().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_credential_map(&self) -> Option<&BTreeMap<String, Credential>> {
|
||||
Some(&self.map)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetIntentToken {
|
||||
map: BTreeMap<Uuid, IntentTokenState>,
|
||||
}
|
||||
|
||||
impl ValueSetIntentToken {
|
||||
pub fn new(t: Uuid, s: IntentTokenState) -> Box<Self> {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(t, s);
|
||||
Box::new(ValueSetIntentToken { map })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, t: Uuid, s: IntentTokenState) -> bool {
|
||||
self.map.insert(t, s).is_none()
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(
|
||||
data: Vec<(Uuid, DbValueIntentTokenStateV1)>,
|
||||
) -> Result<ValueSet, OperationError> {
|
||||
let map = data
|
||||
.into_iter()
|
||||
.map(|(u, dits)| {
|
||||
let ts = match dits {
|
||||
DbValueIntentTokenStateV1::Valid => IntentTokenState::Valid,
|
||||
DbValueIntentTokenStateV1::InProgress(pu, pd) => {
|
||||
IntentTokenState::InProgress(pu, pd)
|
||||
}
|
||||
DbValueIntentTokenStateV1::Consumed => IntentTokenState::Consumed,
|
||||
};
|
||||
(u, ts)
|
||||
})
|
||||
.collect();
|
||||
Ok(Box::new(ValueSetIntentToken { map }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (Uuid, IntentTokenState)>,
|
||||
{
|
||||
let map = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetIntentToken { map }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetIntentToken {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::IntentToken(u, s) => {
|
||||
if let BTreeEntry::Vacant(e) = self.map.entry(u) {
|
||||
e.insert(s);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::IntentToken(u) => self.map.remove(u).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::IntentToken(u) => self.map.contains_key(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.map
|
||||
.keys()
|
||||
.map(|u| u.as_hyphenated().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::IntentToken
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, m)| format!("{}: {:?}", uuid_to_proto_string(*u), m)),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::IntentToken(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, s)| {
|
||||
(
|
||||
*u,
|
||||
match s {
|
||||
IntentTokenState::Valid => DbValueIntentTokenStateV1::Valid,
|
||||
IntentTokenState::InProgress(i, d) => {
|
||||
DbValueIntentTokenStateV1::InProgress(*i, *d)
|
||||
}
|
||||
IntentTokenState::Consumed => DbValueIntentTokenStateV1::Consumed,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.map.keys().cloned().map(PartialValue::IntentToken))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, s)| Value::IntentToken(u.clone(), s.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_intenttoken_map() {
|
||||
&self.map == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_intenttoken_map() {
|
||||
mergemaps!(self.map, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_intenttoken_map(&self) -> Option<&BTreeMap<Uuid, IntentTokenState>> {
|
||||
Some(&self.map)
|
||||
}
|
||||
}
|
160
kanidmd/idm/src/valueset/datetime.rs
Normal file
160
kanidmd/idm/src/valueset/datetime.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetDateTime {
|
||||
set: SmolSet<[OffsetDateTime; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetDateTime {
|
||||
pub fn new(b: OffsetDateTime) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetDateTime { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: OffsetDateTime) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
OffsetDateTime::parse(s, time::Format::Rfc3339)
|
||||
.map(|odt| odt.to_offset(time::UtcOffset::UTC))
|
||||
.map_err(|_| OperationError::InvalidValueState)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(Box::new(ValueSetDateTime { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = OffsetDateTime>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetDateTime { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetDateTime {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::DateTime(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::DateTime(u) => self.set.remove(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::DateTime(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set
|
||||
.iter()
|
||||
.map(|odt| {
|
||||
debug_assert!(odt.offset() == time::UtcOffset::UTC);
|
||||
odt.format(time::Format::Rfc3339)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::DateTime
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|odt| {
|
||||
debug_assert!(odt.offset() == time::UtcOffset::UTC);
|
||||
odt.format(time::Format::Rfc3339)
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::DateTime(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|odt| {
|
||||
debug_assert!(odt.offset() == time::UtcOffset::UTC);
|
||||
odt.format(time::Format::Rfc3339)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::DateTime))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::DateTime))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_datetime_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_datetime_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_datetime_single(&self) -> Option<OffsetDateTime> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().cloned().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_datetime_set(&self) -> Option<&SmolSet<[OffsetDateTime; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
161
kanidmd/idm/src/valueset/iname.rs
Normal file
161
kanidmd/idm/src/valueset/iname.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::value::INAME_RE;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetIname {
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetIname {
|
||||
pub fn new(s: &str) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s.to_lowercase());
|
||||
Box::new(ValueSetIname { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: &str) -> bool {
|
||||
self.set.insert(s.to_lowercase())
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetIname { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<'a, T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = &'a str>,
|
||||
{
|
||||
let set = iter.into_iter().map(str::to_string).collect();
|
||||
Some(Box::new(ValueSetIname { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetIname {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Iname(s) => Ok(self.set.insert(s)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iname(s) => self.set.remove(s),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iname(s) => self.set.contains(s.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.contains(s2)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Utf8StringIname
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.set.iter().all(|s| {
|
||||
match Uuid::parse_str(s) {
|
||||
// It is a uuid, disallow.
|
||||
Ok(_) => false,
|
||||
// Not a uuid, check it against the re.
|
||||
Err(_) => !INAME_RE.is_match(s),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Iname(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().map(|i| PartialValue::new_iname(i.as_str())))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().map(|i| Value::new_iname(i.as_str())))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_iname_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_iname_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_iname_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next().map(|s| s.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_iname_set(&self) -> Option<&BTreeSet<String>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_iname_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
|
||||
fn migrate_iutf8_iname(&self) -> Result<Option<ValueSet>, OperationError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
135
kanidmd/idm/src/valueset/index.rs
Normal file
135
kanidmd/idm/src/valueset/index.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetIndex {
|
||||
set: SmolSet<[IndexType; 3]>,
|
||||
}
|
||||
|
||||
impl ValueSetIndex {
|
||||
pub fn new(s: IndexType) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetIndex { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: IndexType) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<usize>) -> Result<ValueSet, OperationError> {
|
||||
let set: Result<_, _> = data.into_iter().map(IndexType::try_from).collect();
|
||||
let set = set.map_err(|()| OperationError::InvalidValueState)?;
|
||||
Ok(Box::new(ValueSetIndex { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = IndexType>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetIndex { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetIndex {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Index(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Index(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Index(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|b| b.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::INDEX_ID
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|b| b.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::IndexType(self.set.iter().map(|s| s.to_usize()).collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(PartialValue::Index))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(Value::Index))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_index_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_index_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_indextype_iter(&self) -> Option<Box<dyn Iterator<Item = IndexType> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
|
||||
fn as_index_set(&self) -> Option<&SmolSet<[IndexType; 3]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
156
kanidmd/idm/src/valueset/iutf8.rs
Normal file
156
kanidmd/idm/src/valueset/iutf8.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use super::iname::ValueSetIname;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetIutf8 {
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetIutf8 {
|
||||
pub fn new(s: &str) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s.to_lowercase());
|
||||
Box::new(ValueSetIutf8 { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: &str) -> bool {
|
||||
self.set.insert(s.to_lowercase())
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetIutf8 { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<'a, T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = &'a str>,
|
||||
{
|
||||
let set = iter.into_iter().map(str::to_string).collect();
|
||||
Some(Box::new(ValueSetIutf8 { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetIutf8 {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Iutf8(s) => Ok(self.set.insert(s)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iutf8(s) => self.set.remove(s),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iutf8(s) => self.set.contains(s.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Iutf8(s2) => self.set.iter().any(|s1| s1.contains(s2)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Utf8StringInsensitive
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Iutf8(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().map(|i| PartialValue::new_iutf8(i.as_str())))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().map(|i| Value::new_iutf8(i.as_str())))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_iutf8_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_iutf8_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_iutf8_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next().map(|s| s.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_iutf8_set(&self) -> Option<&BTreeSet<String>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_iutf8_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
|
||||
fn migrate_iutf8_iname(&self) -> Result<Option<ValueSet>, OperationError> {
|
||||
let vsi: Option<ValueSet> =
|
||||
ValueSetIname::from_iter(self.set.iter().map(|s| s.as_str())).map(|vs| vs as _);
|
||||
Ok(vsi)
|
||||
}
|
||||
}
|
160
kanidmd/idm/src/valueset/json.rs
Normal file
160
kanidmd/idm/src/valueset/json.rs
Normal file
|
@ -0,0 +1,160 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetJsonFilter {
|
||||
set: SmolSet<[ProtoFilter; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetJsonFilter {
|
||||
pub fn new(b: ProtoFilter) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetJsonFilter { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: ProtoFilter) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data
|
||||
.into_iter()
|
||||
.map(|s| serde_json::from_str(&s).map_err(|_| OperationError::SerdeJsonError))
|
||||
.collect::<Result<_, _>>()?;
|
||||
Ok(Box::new(ValueSetJsonFilter { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = ProtoFilter>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetJsonFilter { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetJsonFilter {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::JsonFilt(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::JsonFilt(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::JsonFilt(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set
|
||||
.iter()
|
||||
.map(|s| {
|
||||
#[allow(clippy::expect_used)]
|
||||
serde_json::to_string(s).expect("A json filter value was corrupted during run-time")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::JSON_FILTER
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|i| {
|
||||
#[allow(clippy::expect_used)]
|
||||
serde_json::to_string(i).expect("A json filter value was corrupted during run-time")
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::JsonFilter(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|s| {
|
||||
#[allow(clippy::expect_used)]
|
||||
serde_json::to_string(s)
|
||||
.expect("A json filter value was corrupted during run-time")
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::JsonFilt))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::JsonFilt))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_json_filter_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_json_filter_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_json_filter_single(&self) -> Option<&ProtoFilter> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_json_filter_set(&self) -> Option<&SmolSet<[ProtoFilter; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
584
kanidmd/idm/src/valueset/mod.rs
Normal file
584
kanidmd/idm/src/valueset/mod.rs
Normal file
|
@ -0,0 +1,584 @@
|
|||
use crate::credential::Credential;
|
||||
use crate::prelude::*;
|
||||
use crate::repl::cid::Cid;
|
||||
use crate::schema::SchemaAttribute;
|
||||
|
||||
use crate::be::dbvalue::DbValueSetV2;
|
||||
use crate::value::Address;
|
||||
use crate::value::IntentTokenState;
|
||||
|
||||
use kanidm_proto::v1::Filter as ProtoFilter;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
use smolset::SmolSet;
|
||||
// use std::fmt::Debug;
|
||||
|
||||
use time::OffsetDateTime;
|
||||
|
||||
mod address;
|
||||
mod binary;
|
||||
mod bool;
|
||||
mod cid;
|
||||
mod cred;
|
||||
mod datetime;
|
||||
mod iname;
|
||||
mod index;
|
||||
mod iutf8;
|
||||
mod json;
|
||||
mod nsuniqueid;
|
||||
mod oauth;
|
||||
mod restricted;
|
||||
mod secret;
|
||||
mod spn;
|
||||
mod ssh;
|
||||
mod syntax;
|
||||
mod uint32;
|
||||
mod url;
|
||||
mod utf8;
|
||||
mod uuid;
|
||||
|
||||
pub use self::address::{ValueSetAddress, ValueSetEmailAddress};
|
||||
pub use self::binary::{ValueSetPrivateBinary, ValueSetPublicBinary};
|
||||
pub use self::bool::ValueSetBool;
|
||||
pub use self::cid::ValueSetCid;
|
||||
pub use self::cred::{ValueSetCredential, ValueSetIntentToken};
|
||||
pub use self::datetime::ValueSetDateTime;
|
||||
pub use self::iname::ValueSetIname;
|
||||
pub use self::index::ValueSetIndex;
|
||||
pub use self::iutf8::ValueSetIutf8;
|
||||
pub use self::json::ValueSetJsonFilter;
|
||||
pub use self::nsuniqueid::ValueSetNsUniqueId;
|
||||
pub use self::oauth::{ValueSetOauthScope, ValueSetOauthScopeMap};
|
||||
pub use self::restricted::ValueSetRestricted;
|
||||
pub use self::secret::ValueSetSecret;
|
||||
pub use self::spn::ValueSetSpn;
|
||||
pub use self::ssh::ValueSetSshKey;
|
||||
pub use self::syntax::ValueSetSyntax;
|
||||
pub use self::uint32::ValueSetUint32;
|
||||
pub use self::url::ValueSetUrl;
|
||||
pub use self::utf8::ValueSetUtf8;
|
||||
pub use self::uuid::ValueSetRefer;
|
||||
pub use self::uuid::ValueSetUuid;
|
||||
|
||||
pub type ValueSet = Box<dyn ValueSetT + Send + Sync + 'static>;
|
||||
|
||||
dyn_clone::clone_trait_object!(ValueSetT);
|
||||
|
||||
pub trait ValueSetT: std::fmt::Debug + DynClone {
|
||||
/// # Safety
|
||||
/// This is unsafe as you are unable to distinguish the case between
|
||||
/// the value already existing, OR the value being an incorrect type to add
|
||||
/// to the set.
|
||||
unsafe fn insert(&mut self, value: Value) -> bool {
|
||||
self.insert_checked(value).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError>;
|
||||
|
||||
fn clear(&mut self);
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool;
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool;
|
||||
|
||||
fn substring(&self, pv: &PartialValue) -> bool;
|
||||
|
||||
fn lessthan(&self, pv: &PartialValue) -> bool;
|
||||
|
||||
fn len(&self) -> usize;
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String>;
|
||||
|
||||
fn syntax(&self) -> SyntaxType;
|
||||
|
||||
fn validate(&self, schema_attr: &SchemaAttribute) -> bool;
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_>;
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2;
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_>;
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_>;
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool;
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError>;
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
fn migrate_iutf8_iname(&self) -> Result<Option<ValueSet>, OperationError> {
|
||||
debug_assert!(false);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_ssh_tag(&self, _tag: &str) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_ref_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_utf8_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
error!("as_utf8_iter should not be called on {:?}", self.syntax());
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_iutf8_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
error!("as_iutf8_iter should not be called on {:?}", self.syntax());
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_iname_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
error!("as_iname_iter should not be called on {:?}", self.syntax());
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_indextype_iter(&self) -> Option<Box<dyn Iterator<Item = IndexType> + '_>> {
|
||||
error!(
|
||||
"as_indextype_set should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_restricted_string_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
error!(
|
||||
"as_restricted_string_iter should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_oauthscope_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
error!(
|
||||
"as_oauthscope_iter should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_sshpubkey_str_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_email_str_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_utf8_set(&self) -> Option<&BTreeSet<String>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_iutf8_set(&self) -> Option<&BTreeSet<String>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_iname_set(&self) -> Option<&BTreeSet<String>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_uuid_set(&self) -> Option<&SmolSet<[Uuid; 1]>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_refer_set(&self) -> Option<&BTreeSet<Uuid>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_bool_set(&self) -> Option<&SmolSet<[bool; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_uint32_set(&self) -> Option<&SmolSet<[u32; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_syntax_set(&self) -> Option<&SmolSet<[SyntaxType; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_index_set(&self) -> Option<&SmolSet<[IndexType; 3]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_secret_set(&self) -> Option<&SmolSet<[String; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_restricted_string_set(&self) -> Option<&BTreeSet<String>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_spn_set(&self) -> Option<&SmolSet<[(String, String); 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_cid_set(&self) -> Option<&SmolSet<[Cid; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_json_filter_set(&self) -> Option<&SmolSet<[ProtoFilter; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_nsuniqueid_set(&self) -> Option<&SmolSet<[String; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_url_set(&self) -> Option<&SmolSet<[Url; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_datetime_set(&self) -> Option<&SmolSet<[OffsetDateTime; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_private_binary_set(&self) -> Option<&SmolSet<[Vec<u8>; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_oauthscope_set(&self) -> Option<&BTreeSet<String>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_address_set(&self) -> Option<&SmolSet<[Address; 1]>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_credential_map(&self) -> Option<&BTreeMap<String, Credential>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_emailaddress_set(&self) -> Option<(&String, &BTreeSet<String>)> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_sshkey_map(&self) -> Option<&BTreeMap<String, String>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_oauthscopemap(&self) -> Option<&BTreeMap<Uuid, BTreeSet<String>>> {
|
||||
error!(
|
||||
"as_oauthscopemap should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_publicbinary_map(&self) -> Option<&BTreeMap<String, Vec<u8>>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn as_intenttoken_map(&self) -> Option<&BTreeMap<Uuid, IntentTokenState>> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_value_single(&self) -> Option<Value> {
|
||||
if self.len() != 1 {
|
||||
None
|
||||
} else {
|
||||
self.to_value_iter().take(1).next()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_proto_string_single(&self) -> Option<String> {
|
||||
if self.len() != 1 {
|
||||
None
|
||||
} else {
|
||||
self.to_proto_string_clone_iter().take(1).next()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_uuid_single(&self) -> Option<Uuid> {
|
||||
error!("to_uuid_single should not be called on {:?}", self.syntax());
|
||||
None
|
||||
}
|
||||
|
||||
fn to_refer_single(&self) -> Option<Uuid> {
|
||||
error!(
|
||||
"to_refer_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_bool_single(&self) -> Option<bool> {
|
||||
error!("to_bool_single should not be called on {:?}", self.syntax());
|
||||
None
|
||||
}
|
||||
|
||||
fn to_uint32_single(&self) -> Option<u32> {
|
||||
error!(
|
||||
"to_uint32_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_syntaxtype_single(&self) -> Option<SyntaxType> {
|
||||
error!(
|
||||
"to_syntaxtype_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_credential_single(&self) -> Option<&Credential> {
|
||||
error!(
|
||||
"to_credential_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_secret_single(&self) -> Option<&str> {
|
||||
error!(
|
||||
"to_secret_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_restricted_string_single(&self) -> Option<&str> {
|
||||
error!(
|
||||
"to_restricted_string_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_utf8_single(&self) -> Option<&str> {
|
||||
error!("to_utf8_single should not be called on {:?}", self.syntax());
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_iutf8_single(&self) -> Option<&str> {
|
||||
error!(
|
||||
"to_iutf8_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_iname_single(&self) -> Option<&str> {
|
||||
error!(
|
||||
"to_iname_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_datetime_single(&self) -> Option<OffsetDateTime> {
|
||||
error!(
|
||||
"to_datetime_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_url_single(&self) -> Option<&Url> {
|
||||
error!("to_url_single should not be called on {:?}", self.syntax());
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_json_filter_single(&self) -> Option<&ProtoFilter> {
|
||||
error!(
|
||||
"to_json_filter_single should not be called on {:?}",
|
||||
self.syntax()
|
||||
);
|
||||
// debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_email_address_primary_str(&self) -> Option<&str> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
|
||||
fn to_private_binary_single(&self) -> Option<&[u8]> {
|
||||
debug_assert!(false);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ValueSet {
|
||||
fn eq(&self, other: &ValueSet) -> bool {
|
||||
self.equal(other)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uuid_to_proto_string(u: Uuid) -> String {
|
||||
u.as_hyphenated().to_string()
|
||||
}
|
||||
|
||||
pub fn from_result_value_iter(
|
||||
mut iter: impl Iterator<Item = Result<Value, OperationError>>,
|
||||
) -> Result<ValueSet, OperationError> {
|
||||
let init = if let Some(v) = iter.next() {
|
||||
v
|
||||
} else {
|
||||
admin_error!("Empty value iterator");
|
||||
return Err(OperationError::InvalidValueState);
|
||||
};
|
||||
|
||||
let init = init?;
|
||||
|
||||
let mut vs: ValueSet = match init {
|
||||
Value::Utf8(s) => ValueSetUtf8::new(s),
|
||||
Value::Iutf8(s) => ValueSetIutf8::new(&s),
|
||||
Value::Iname(s) => ValueSetIname::new(&s),
|
||||
Value::Uuid(u) => ValueSetUuid::new(u),
|
||||
Value::Refer(u) => ValueSetRefer::new(u),
|
||||
Value::Bool(u) => ValueSetBool::new(u),
|
||||
Value::Uint32(u) => ValueSetUint32::new(u),
|
||||
Value::Syntax(u) => ValueSetSyntax::new(u),
|
||||
Value::Index(u) => ValueSetIndex::new(u),
|
||||
Value::SecretValue(u) => ValueSetSecret::new(u),
|
||||
Value::RestrictedString(u) => ValueSetRestricted::new(u),
|
||||
Value::Spn(n, d) => ValueSetSpn::new((n, d)),
|
||||
Value::Cid(u) => ValueSetCid::new(u),
|
||||
Value::JsonFilt(u) => ValueSetJsonFilter::new(u),
|
||||
Value::Nsuniqueid(u) => ValueSetNsUniqueId::new(u),
|
||||
Value::Url(u) => ValueSetUrl::new(u),
|
||||
Value::DateTime(u) => ValueSetDateTime::new(u),
|
||||
Value::PrivateBinary(u) => ValueSetPrivateBinary::new(u),
|
||||
Value::OauthScope(u) => ValueSetOauthScope::new(u),
|
||||
Value::Address(u) => ValueSetAddress::new(u),
|
||||
Value::Cred(t, c) => ValueSetCredential::new(t, c),
|
||||
Value::SshKey(t, k) => ValueSetSshKey::new(t, k),
|
||||
Value::OauthScopeMap(u, m) => ValueSetOauthScopeMap::new(u, m),
|
||||
Value::PublicBinary(t, b) => ValueSetPublicBinary::new(t, b),
|
||||
Value::IntentToken(u, s) => ValueSetIntentToken::new(u, s),
|
||||
Value::EmailAddress(a, _) => ValueSetEmailAddress::new(a),
|
||||
_ => return Err(OperationError::InvalidValueState),
|
||||
};
|
||||
|
||||
for maybe_v in iter {
|
||||
let v = maybe_v?;
|
||||
// Need to error if wrong type (but shouldn't be due to the way qs works)
|
||||
vs.insert_checked(v)?;
|
||||
}
|
||||
Ok(vs)
|
||||
}
|
||||
|
||||
pub fn from_value_iter(mut iter: impl Iterator<Item = Value>) -> Result<ValueSet, OperationError> {
|
||||
let init = if let Some(v) = iter.next() {
|
||||
v
|
||||
} else {
|
||||
admin_error!("Empty value iterator");
|
||||
return Err(OperationError::InvalidValueState);
|
||||
};
|
||||
|
||||
let mut vs: ValueSet = match init {
|
||||
Value::Utf8(s) => ValueSetUtf8::new(s),
|
||||
Value::Iutf8(s) => ValueSetIutf8::new(&s),
|
||||
Value::Iname(s) => ValueSetIname::new(&s),
|
||||
Value::Uuid(u) => ValueSetUuid::new(u),
|
||||
Value::Refer(u) => ValueSetRefer::new(u),
|
||||
Value::Bool(u) => ValueSetBool::new(u),
|
||||
Value::Uint32(u) => ValueSetUint32::new(u),
|
||||
Value::Syntax(u) => ValueSetSyntax::new(u),
|
||||
Value::Index(u) => ValueSetIndex::new(u),
|
||||
Value::SecretValue(u) => ValueSetSecret::new(u),
|
||||
Value::RestrictedString(u) => ValueSetRestricted::new(u),
|
||||
Value::Spn(n, d) => ValueSetSpn::new((n, d)),
|
||||
Value::Cid(u) => ValueSetCid::new(u),
|
||||
Value::JsonFilt(u) => ValueSetJsonFilter::new(u),
|
||||
Value::Nsuniqueid(u) => ValueSetNsUniqueId::new(u),
|
||||
Value::Url(u) => ValueSetUrl::new(u),
|
||||
Value::DateTime(u) => ValueSetDateTime::new(u),
|
||||
Value::PrivateBinary(u) => ValueSetPrivateBinary::new(u),
|
||||
Value::OauthScope(u) => ValueSetOauthScope::new(u),
|
||||
Value::Address(u) => ValueSetAddress::new(u),
|
||||
Value::Cred(t, c) => ValueSetCredential::new(t, c),
|
||||
Value::SshKey(t, k) => ValueSetSshKey::new(t, k),
|
||||
Value::OauthScopeMap(u, m) => ValueSetOauthScopeMap::new(u, m),
|
||||
Value::PublicBinary(t, b) => ValueSetPublicBinary::new(t, b),
|
||||
Value::IntentToken(u, s) => ValueSetIntentToken::new(u, s),
|
||||
Value::EmailAddress(a, _) => ValueSetEmailAddress::new(a),
|
||||
_ => return Err(OperationError::InvalidValueState),
|
||||
};
|
||||
|
||||
for v in iter {
|
||||
vs.insert_checked(v)?;
|
||||
}
|
||||
Ok(vs)
|
||||
}
|
||||
|
||||
pub fn from_db_valueset_v2(dbvs: DbValueSetV2) -> Result<ValueSet, OperationError> {
|
||||
match dbvs {
|
||||
DbValueSetV2::Utf8(set) => ValueSetUtf8::from_dbvs2(set),
|
||||
DbValueSetV2::Iutf8(set) => ValueSetIutf8::from_dbvs2(set),
|
||||
DbValueSetV2::Iname(set) => ValueSetIname::from_dbvs2(set),
|
||||
DbValueSetV2::Uuid(set) => ValueSetUuid::from_dbvs2(set),
|
||||
DbValueSetV2::Reference(set) => ValueSetRefer::from_dbvs2(set),
|
||||
DbValueSetV2::Bool(set) => ValueSetBool::from_dbvs2(set),
|
||||
DbValueSetV2::Uint32(set) => ValueSetUint32::from_dbvs2(set),
|
||||
DbValueSetV2::SyntaxType(set) => ValueSetSyntax::from_dbvs2(set),
|
||||
DbValueSetV2::IndexType(set) => ValueSetIndex::from_dbvs2(set),
|
||||
DbValueSetV2::SecretValue(set) => ValueSetSecret::from_dbvs2(set),
|
||||
DbValueSetV2::RestrictedString(set) => ValueSetRestricted::from_dbvs2(set),
|
||||
DbValueSetV2::Spn(set) => ValueSetSpn::from_dbvs2(set),
|
||||
DbValueSetV2::Cid(set) => ValueSetCid::from_dbvs2(set),
|
||||
DbValueSetV2::JsonFilter(set) => ValueSetJsonFilter::from_dbvs2(set),
|
||||
DbValueSetV2::NsUniqueId(set) => ValueSetNsUniqueId::from_dbvs2(set),
|
||||
DbValueSetV2::Url(set) => ValueSetUrl::from_dbvs2(set),
|
||||
DbValueSetV2::DateTime(set) => ValueSetDateTime::from_dbvs2(set),
|
||||
DbValueSetV2::PrivateBinary(set) => ValueSetPrivateBinary::from_dbvs2(set),
|
||||
DbValueSetV2::OauthScope(set) => ValueSetOauthScope::from_dbvs2(set),
|
||||
DbValueSetV2::Address(set) => ValueSetAddress::from_dbvs2(set),
|
||||
DbValueSetV2::Credential(set) => ValueSetCredential::from_dbvs2(set),
|
||||
DbValueSetV2::SshKey(set) => ValueSetSshKey::from_dbvs2(set),
|
||||
DbValueSetV2::OauthScopeMap(set) => ValueSetOauthScopeMap::from_dbvs2(set),
|
||||
DbValueSetV2::PublicBinary(set) => ValueSetPublicBinary::from_dbvs2(set),
|
||||
DbValueSetV2::IntentToken(set) => ValueSetIntentToken::from_dbvs2(set),
|
||||
DbValueSetV2::EmailAddress(primary, set) => ValueSetEmailAddress::from_dbvs2(primary, set),
|
||||
/*
|
||||
DbValueSetV2::PhoneNumber(set) =>
|
||||
DbValueSetV2::TrustedDeviceEnrollment(set) =>
|
||||
DbValueSetV2::AuthSession(set) =>
|
||||
*/
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
141
kanidmd/idm/src/valueset/nsuniqueid.rs
Normal file
141
kanidmd/idm/src/valueset/nsuniqueid.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::value::NSUNIQUEID_RE;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetNsUniqueId {
|
||||
set: SmolSet<[String; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetNsUniqueId {
|
||||
pub fn new(b: String) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetNsUniqueId { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: String) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetNsUniqueId { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = String>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetNsUniqueId { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetNsUniqueId {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Nsuniqueid(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Nsuniqueid(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Nsuniqueid(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::NsUniqueId
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.set.iter().all(|s| NSUNIQUEID_RE.is_match(s))
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::NsUniqueId(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::Nsuniqueid))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::Nsuniqueid))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_nsuniqueid_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_nsuniqueid_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn to_nsuniqueid_single(&self) -> Option<&String> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn as_nsuniqueid_set(&self) -> Option<&SmolSet<[String; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
312
kanidmd/idm/src/valueset/oauth.rs
Normal file
312
kanidmd/idm/src/valueset/oauth.rs
Normal file
|
@ -0,0 +1,312 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::be::dbvalue::DbValueOauthScopeMapV1;
|
||||
use crate::valueset::uuid_to_proto_string;
|
||||
use std::collections::btree_map::Entry as BTreeEntry;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::value::OAUTHSCOPE_RE;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetOauthScope {
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetOauthScope {
|
||||
pub fn new(s: String) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetOauthScope { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: String) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetOauthScope { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<'a, T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = String>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetOauthScope { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetOauthScope {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::OauthScope(s) => Ok(self.set.insert(s)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::OauthScope(s) => self.set.remove(s.as_str()),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::OauthScope(s) => self.set.contains(s.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::OauthScope
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.set.iter().all(|s| OAUTHSCOPE_RE.is_match(s))
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::OauthScope(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::OauthScope))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::OauthScope))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_oauthscope_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_oauthscope_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn to_oauthscope_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next().map(|s| s.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn as_oauthscope_set(&self) -> Option<&BTreeSet<String>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_oauthscope_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetOauthScopeMap {
|
||||
map: BTreeMap<Uuid, BTreeSet<String>>,
|
||||
}
|
||||
|
||||
impl ValueSetOauthScopeMap {
|
||||
pub fn new(u: Uuid, m: BTreeSet<String>) -> Box<Self> {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(u, m);
|
||||
Box::new(ValueSetOauthScopeMap { map })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, u: Uuid, m: BTreeSet<String>) -> bool {
|
||||
self.map.insert(u, m).is_none()
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbValueOauthScopeMapV1>) -> Result<ValueSet, OperationError> {
|
||||
let map = data
|
||||
.into_iter()
|
||||
.map(|dbv| {
|
||||
let u = dbv.refer;
|
||||
let m = dbv.data.into_iter().collect();
|
||||
(u, m)
|
||||
})
|
||||
.collect();
|
||||
Ok(Box::new(ValueSetOauthScopeMap { map }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (Uuid, BTreeSet<String>)>,
|
||||
{
|
||||
let map = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetOauthScopeMap { map }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetOauthScopeMap {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::OauthScopeMap(u, m) => {
|
||||
if let BTreeEntry::Vacant(e) = self.map.entry(u) {
|
||||
e.insert(m);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::OauthScopeMap(u) | PartialValue::Refer(u) => self.map.remove(u).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::OauthScopeMap(u) | PartialValue::Refer(u) => self.map.contains_key(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.map
|
||||
.keys()
|
||||
.map(|u| u.as_hyphenated().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::OauthScopeMap
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.map
|
||||
.values()
|
||||
.map(|set| set.iter())
|
||||
.flatten()
|
||||
.all(|s| OAUTHSCOPE_RE.is_match(s))
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, m)| format!("{}: {:?}", uuid_to_proto_string(*u), m)),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::OauthScopeMap(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, m)| DbValueOauthScopeMapV1 {
|
||||
refer: *u,
|
||||
data: m.iter().cloned().collect(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.map.keys().cloned().map(PartialValue::OauthScopeMap))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(u, m)| Value::OauthScopeMap(*u, m.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_oauthscopemap() {
|
||||
&self.map == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_oauthscopemap() {
|
||||
mergemaps!(self.map, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_oauthscopemap(&self) -> Option<&BTreeMap<Uuid, BTreeSet<String>>> {
|
||||
Some(&self.map)
|
||||
}
|
||||
|
||||
fn as_ref_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
|
||||
// This is what ties us as a type that can be refint checked.
|
||||
Some(Box::new(self.map.keys().copied()))
|
||||
}
|
||||
}
|
152
kanidmd/idm/src/valueset/restricted.rs
Normal file
152
kanidmd/idm/src/valueset/restricted.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetRestricted {
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetRestricted {
|
||||
pub fn new(s: String) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetRestricted { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: String) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetRestricted { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<'a, T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = String>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetRestricted { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetRestricted {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::RestrictedString(s) => Ok(self.set.insert(s)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::RestrictedString(s) => self.set.remove(s),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::RestrictedString(s) => self.set.contains(s.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::RestrictedString(s2) => self.set.iter().any(|s1| s1.contains(s2)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
unreachable!();
|
||||
// SyntaxType::RestrictedString
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
self.set.iter().all(|_s| {
|
||||
// schema_attr.re_pattern.is_match(s)
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::RestrictedString(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::RestrictedString))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::RestrictedString))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_restricted_string_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_restricted_string_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_restricted_string_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next().map(|s| s.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_restricted_string_set(&self) -> Option<&BTreeSet<String>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_restricted_string_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
}
|
129
kanidmd/idm/src/valueset/secret.rs
Normal file
129
kanidmd/idm/src/valueset/secret.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetSecret {
|
||||
set: SmolSet<[String; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetSecret {
|
||||
pub fn new(b: String) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetSecret { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: String) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetSecret { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = String>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetSecret { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetSecret {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::SecretValue(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn contains(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::SecretUtf8String
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|_| "hidden".to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::SecretValue(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().map(|_| PartialValue::SecretValue))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::SecretValue))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_secret_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_secret_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_secret_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().map(|s| s.as_str()).take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_secret_set(&self) -> Option<&SmolSet<[String; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
158
kanidmd/idm/src/valueset/spn.rs
Normal file
158
kanidmd/idm/src/valueset/spn.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetSpn {
|
||||
set: SmolSet<[(String, String); 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetSpn {
|
||||
pub fn new(u: (String, String)) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(u);
|
||||
Box::new(ValueSetSpn { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, u: (String, String)) -> bool {
|
||||
self.set.insert(u)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<(String, String)>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetSpn { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (String, String)>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetSpn { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetSpn {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Spn(n, d) => Ok(self.set.insert((n, d))),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Spn(n, d) => self.set.remove(&(n.clone(), d.clone())),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Spn(n, d) => self.set.contains(&(n.clone(), d.clone())),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set
|
||||
.iter()
|
||||
.map(|(n, d)| format!("{}@{}", n, d))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::SecurityPrincipalName
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|(n, d)| format!("{}@{}", n, d)))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Spn(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|(n, d)| PartialValue::Spn(n.clone(), d.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.map(|(n, d)| Value::Spn(n.clone(), d.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_spn_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_spn_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn to_spn_single(&self) -> Option<> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn as_spn_set(&self) -> Option<&SmolSet<[(String, String); 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
/*
|
||||
fn as_spn_iter(&self) -> Option<Box<dyn Iterator<Item = Spn> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
*/
|
||||
}
|
154
kanidmd/idm/src/valueset/ssh.rs
Normal file
154
kanidmd/idm/src/valueset/ssh.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::btree_map::Entry as BTreeEntry;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::be::dbvalue::DbValueTaggedStringV1;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetSshKey {
|
||||
map: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl ValueSetSshKey {
|
||||
pub fn new(t: String, k: String) -> Box<Self> {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(t, k);
|
||||
Box::new(ValueSetSshKey { map })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, t: String, k: String) -> bool {
|
||||
self.map.insert(t, k).is_none()
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbValueTaggedStringV1>) -> Result<ValueSet, OperationError> {
|
||||
let map = data.into_iter().map(|dbv| (dbv.tag, dbv.data)).collect();
|
||||
Ok(Box::new(ValueSetSshKey { map }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = (String, String)>,
|
||||
{
|
||||
let map = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetSshKey { map }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetSshKey {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::SshKey(t, k) => {
|
||||
if let BTreeEntry::Vacant(e) = self.map.entry(t) {
|
||||
e.insert(k);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::SshKey(t) => self.map.remove(t.as_str()).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::SshKey(t) => self.map.contains_key(t.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.map.keys().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::SshKey
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.map.keys().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::SshKey(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(tag, key)| DbValueTaggedStringV1 {
|
||||
tag: tag.clone(),
|
||||
data: key.clone(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.map.keys().cloned().map(PartialValue::SshKey))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(
|
||||
self.map
|
||||
.iter()
|
||||
.map(|(t, k)| Value::SshKey(t.clone(), k.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_sshkey_map() {
|
||||
&self.map == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_sshkey_map() {
|
||||
mergemaps!(self.map, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn as_sshkey_map(&self) -> Option<&BTreeMap<String, String>> {
|
||||
Some(&self.map)
|
||||
}
|
||||
|
||||
fn get_ssh_tag(&self, tag: &str) -> Option<&str> {
|
||||
self.map.get(tag).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
fn as_sshpubkey_str_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.map.values().map(|s| s.as_str())))
|
||||
}
|
||||
}
|
139
kanidmd/idm/src/valueset/syntax.rs
Normal file
139
kanidmd/idm/src/valueset/syntax.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetSyntax {
|
||||
set: SmolSet<[SyntaxType; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetSyntax {
|
||||
pub fn new(s: SyntaxType) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetSyntax { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: SyntaxType) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<usize>) -> Result<ValueSet, OperationError> {
|
||||
let set: Result<_, _> = data.into_iter().map(SyntaxType::try_from).collect();
|
||||
let set = set.map_err(|()| OperationError::InvalidValueState)?;
|
||||
Ok(Box::new(ValueSetSyntax { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = SyntaxType>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetSyntax { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetSyntax {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Syntax(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Syntax(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Syntax(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|b| b.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::SYNTAX_ID
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|b| b.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::SyntaxType(self.set.iter().map(|s| s.to_usize()).collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(PartialValue::Syntax))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(Value::Syntax))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_syntax_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_syntax_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_syntaxtype_single(&self) -> Option<SyntaxType> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_syntax_set(&self) -> Option<&SmolSet<[SyntaxType; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
159
kanidmd/idm/src/valueset/uint32.rs
Normal file
159
kanidmd/idm/src/valueset/uint32.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetUint32 {
|
||||
set: SmolSet<[u32; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetUint32 {
|
||||
pub fn new(b: u32) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetUint32 { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: u32) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<u32>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetUint32 { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = u32>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetUint32 { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetUint32 {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Uint32(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Uint32(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Uint32(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Uint32(u) => self.set.iter().any(|i| i < u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|b| b.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::UINT32
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|b| b.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Uint32(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(
|
||||
self.set
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|b| PartialValue::new_uint32(b)),
|
||||
)
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|b| Value::new_uint32(b)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_uint32_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_uint32_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_uint32_single(&self) -> Option<u32> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uint32_set(&self) -> Option<&SmolSet<[u32; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_valueset_basic() {
|
||||
let mut vs = ValueSetUint32::new(0);
|
||||
assert!(vs.insert_checked(Value::new_uint32(0)) == Ok(false));
|
||||
assert!(vs.insert_checked(Value::new_uint32(1)) == Ok(true));
|
||||
assert!(vs.insert_checked(Value::new_uint32(1)) == Ok(false));
|
||||
}
|
||||
}
|
135
kanidmd/idm/src/valueset/url.rs
Normal file
135
kanidmd/idm/src/valueset/url.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetUrl {
|
||||
set: SmolSet<[Url; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetUrl {
|
||||
pub fn new(b: Url) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(b);
|
||||
Box::new(ValueSetUrl { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, b: Url) -> bool {
|
||||
self.set.insert(b)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<Url>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetUrl { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Url>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetUrl { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetUrl {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Url(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Url(u) => self.set.remove(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Url(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().map(|u| u.to_string()).collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Url
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().map(|i| i.to_string()))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Url(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(PartialValue::Url))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().cloned().map(Value::Url))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_url_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_url_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_url_single(&self) -> Option<&Url> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_url_set(&self) -> Option<&SmolSet<[Url; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
}
|
137
kanidmd/idm/src/valueset/utf8.rs
Normal file
137
kanidmd/idm/src/valueset/utf8.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetUtf8 {
|
||||
set: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl ValueSetUtf8 {
|
||||
pub fn new(s: String) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(s);
|
||||
Box::new(ValueSetUtf8 { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, s: String) -> bool {
|
||||
self.set.insert(s)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<String>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetUtf8 { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetUtf8 {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Utf8(s) => Ok(self.set.insert(s)),
|
||||
_ => Err(OperationError::InvalidValueState),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Utf8(s) => self.set.remove(s),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Utf8(s) => self.set.contains(s.as_str()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Utf8(s2) => self.set.iter().any(|s1| s1.contains(s2)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::UTF8STRING
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().cloned())
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Utf8(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().map(|i| PartialValue::new_utf8s(i.as_str())))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().map(|i| Value::new_utf8s(i.as_str())))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_utf8_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_utf8_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_utf8_single(&self) -> Option<&str> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().take(1).next().map(|s| s.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_utf8_set(&self) -> Option<&BTreeSet<String>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_utf8_iter(&self) -> Option<Box<dyn Iterator<Item = &str> + '_>> {
|
||||
Some(Box::new(self.set.iter().map(|s| s.as_str())))
|
||||
}
|
||||
}
|
290
kanidmd/idm/src/valueset/uuid.rs
Normal file
290
kanidmd/idm/src/valueset/uuid.rs
Normal file
|
@ -0,0 +1,290 @@
|
|||
use crate::prelude::*;
|
||||
use crate::schema::SchemaAttribute;
|
||||
use crate::valueset::uuid_to_proto_string;
|
||||
use crate::valueset::DbValueSetV2;
|
||||
use crate::valueset::ValueSet;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use smolset::SmolSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetUuid {
|
||||
set: SmolSet<[Uuid; 1]>,
|
||||
}
|
||||
|
||||
impl ValueSetUuid {
|
||||
pub fn new(u: Uuid) -> Box<Self> {
|
||||
let mut set = SmolSet::new();
|
||||
set.insert(u);
|
||||
Box::new(ValueSetUuid { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, u: Uuid) -> bool {
|
||||
self.set.insert(u)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<Uuid>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetUuid { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Uuid>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetUuid { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetUuid {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Uuid(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Uuid(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Uuid(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set
|
||||
.iter()
|
||||
.map(|u| u.as_hyphenated().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::Uuid
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().copied().map(uuid_to_proto_string))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Uuid(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|u| PartialValue::new_uuid(u)))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|u| Value::new_uuid(u)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_uuid_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_uuid_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_uuid_single(&self) -> Option<Uuid> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_uuid_set(&self) -> Option<&SmolSet<[Uuid; 1]>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
/*
|
||||
fn as_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValueSetRefer {
|
||||
set: BTreeSet<Uuid>,
|
||||
}
|
||||
|
||||
impl ValueSetRefer {
|
||||
pub fn new(u: Uuid) -> Box<Self> {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(u);
|
||||
Box::new(ValueSetRefer { set })
|
||||
}
|
||||
|
||||
pub fn push(&mut self, u: Uuid) -> bool {
|
||||
self.set.insert(u)
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<Uuid>) -> Result<ValueSet, OperationError> {
|
||||
let set = data.into_iter().collect();
|
||||
Ok(Box::new(ValueSetRefer { set }))
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
|
||||
where
|
||||
T: IntoIterator<Item = Uuid>,
|
||||
{
|
||||
let set = iter.into_iter().collect();
|
||||
Some(Box::new(ValueSetRefer { set }))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSetT for ValueSetRefer {
|
||||
fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
|
||||
match value {
|
||||
Value::Refer(u) => Ok(self.set.insert(u)),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
|
||||
fn remove(&mut self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Refer(u) => self.set.remove(u),
|
||||
_ => {
|
||||
debug_assert!(false);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, pv: &PartialValue) -> bool {
|
||||
match pv {
|
||||
PartialValue::Refer(u) => self.set.contains(u),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn substring(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn lessthan(&self, _pv: &PartialValue) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
|
||||
fn generate_idx_eq_keys(&self) -> Vec<String> {
|
||||
self.set
|
||||
.iter()
|
||||
.map(|u| u.as_hyphenated().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> SyntaxType {
|
||||
SyntaxType::REFERENCE_UUID
|
||||
}
|
||||
|
||||
fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
|
||||
Box::new(self.set.iter().copied().map(uuid_to_proto_string))
|
||||
}
|
||||
|
||||
fn to_db_valueset_v2(&self) -> DbValueSetV2 {
|
||||
DbValueSetV2::Reference(self.set.iter().cloned().collect())
|
||||
}
|
||||
|
||||
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|u| PartialValue::new_refer(u)))
|
||||
}
|
||||
|
||||
fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
|
||||
Box::new(self.set.iter().copied().map(|u| Value::new_refer(u)))
|
||||
}
|
||||
|
||||
fn equal(&self, other: &ValueSet) -> bool {
|
||||
if let Some(other) = other.as_refer_set() {
|
||||
&self.set == other
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
|
||||
if let Some(b) = other.as_refer_set() {
|
||||
mergesets!(self.set, b)
|
||||
} else {
|
||||
debug_assert!(false);
|
||||
Err(OperationError::InvalidValueState)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_refer_single(&self) -> Option<Uuid> {
|
||||
if self.set.len() == 1 {
|
||||
self.set.iter().copied().take(1).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_refer_set(&self) -> Option<&BTreeSet<Uuid>> {
|
||||
Some(&self.set)
|
||||
}
|
||||
|
||||
fn as_ref_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
|
||||
Some(Box::new(self.set.iter().copied()))
|
||||
}
|
||||
}
|
|
@ -808,7 +808,7 @@ pub async fn group_get_id_unix_token(req: tide::Request<AppState>) -> tide::Resu
|
|||
}
|
||||
|
||||
pub async fn domain_get(req: tide::Request<AppState>) -> tide::Result {
|
||||
let filter = filter_all!(f_eq("uuid", PartialValue::new_uuidr(&UUID_DOMAIN_INFO)));
|
||||
let filter = filter_all!(f_eq("uuid", PartialValue::new_uuid(*UUID_DOMAIN_INFO)));
|
||||
json_rest_event_get(req, filter, None).await
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ name = "orca"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
jemallocator = { version = "0.3.0", optional = true }
|
||||
tikv-jemallocator = "0.4.0"
|
||||
tracing = "^0.1.34"
|
||||
tracing-subscriber = "^0.3.11"
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
#[cfg(all(jemallocator, not(test)))]
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
|
Loading…
Reference in a new issue