Scim add EntryReference (#3079)

Allow references to be displayed as a complex object
This commit is contained in:
Merlijn 2024-10-10 02:13:45 +02:00 committed by GitHub
parent 12236a39f5
commit 4e125b5043
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 386 additions and 186 deletions

View file

@ -180,6 +180,14 @@ pub struct ScimOAuth2ClaimMap {
pub values: BTreeSet<String>, pub values: BTreeSet<String>,
} }
#[serde_as]
#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ScimReference {
pub uuid: Uuid,
pub value: String,
}
/// This is a strongly typed ScimValue for Kanidm. It is for serialisation only /// This is a strongly typed ScimValue for Kanidm. It is for serialisation only
/// since on a deserialisation path we can not know the intent of the sender /// since on a deserialisation path we can not know the intent of the sender
/// to how we deserialise strings. Additionally during deserialisation we need /// to how we deserialise strings. Additionally during deserialisation we need
@ -196,6 +204,9 @@ pub enum ScimValueKanidm {
DateTime(#[serde_as(as = "Rfc3339")] OffsetDateTime), DateTime(#[serde_as(as = "Rfc3339")] OffsetDateTime),
Reference(Url), Reference(Url),
Uuid(Uuid), Uuid(Uuid),
EntryReference(ScimReference),
EntryReferences(Vec<ScimReference>),
// Other strong outbound types. // Other strong outbound types.
ArrayString(Vec<String>), ArrayString(Vec<String>),
ArrayDateTime(#[serde_as(as = "Vec<Rfc3339>")] Vec<OffsetDateTime>), ArrayDateTime(#[serde_as(as = "Vec<Rfc3339>")] Vec<OffsetDateTime>),

View file

@ -227,6 +227,6 @@ impl QueryServerReadV1 {
idms_prox_read idms_prox_read
.qs_read .qs_read
.impersonate_search_ext_uuid(target_uuid, &ident) .impersonate_search_ext_uuid(target_uuid, &ident)
.and_then(|entry| entry.to_scim_kanidm()) .and_then(|entry| entry.to_scim_kanidm(idms_prox_read.qs_read))
} }
} }

View file

@ -29,23 +29,6 @@ pub use std::collections::BTreeSet as Set;
use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet}; use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
use std::sync::Arc; use std::sync::Arc;
use compact_jwt::JwsEs256Signer;
use hashbrown::{HashMap, HashSet};
use kanidm_proto::internal::ImageValue;
use kanidm_proto::internal::{
ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
};
use kanidm_proto::v1::Entry as ProtoEntry;
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
use openssl::ec::EcKey;
use openssl::pkey::{Private, Public};
use time::OffsetDateTime;
use tracing::trace;
use uuid::Uuid;
use webauthn_rs::prelude::{
AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
};
use crate::be::dbentry::{DbEntry, DbEntryVers}; use crate::be::dbentry::{DbEntry, DbEntryVers};
use crate::be::dbvalue::DbValueSetV2; use crate::be::dbvalue::DbValueSetV2;
use crate::be::{IdxKey, IdxSlope}; use crate::be::{IdxKey, IdxSlope};
@ -58,13 +41,30 @@ use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::repl::entry::EntryChangeState; use crate::repl::entry::EntryChangeState;
use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1}; use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1};
use compact_jwt::JwsEs256Signer;
use hashbrown::{HashMap, HashSet};
use kanidm_proto::internal::ImageValue;
use kanidm_proto::internal::{
ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
};
use kanidm_proto::scim_v1::server::ScimReference;
use kanidm_proto::v1::Entry as ProtoEntry;
use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
use openssl::ec::EcKey;
use openssl::pkey::{Private, Public};
use time::OffsetDateTime;
use tracing::trace;
use uuid::Uuid;
use webauthn_rs::prelude::{
AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
};
use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction}; use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
use crate::value::{ use crate::value::{
ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session, ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session,
SyntaxType, Value, SyntaxType, Value,
}; };
use crate::valueset::{self, ValueSet}; use crate::valueset::{self, ScimResolveStatus, ScimValueIntermediate, ValueSet};
pub type EntryInitNew = Entry<EntryInit, EntryNew>; pub type EntryInitNew = Entry<EntryInit, EntryNew>;
pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>; pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
@ -2230,15 +2230,32 @@ impl Entry<EntryReduced, EntryCommitted> {
Ok(ProtoEntry { attrs: attrs? }) Ok(ProtoEntry { attrs: attrs? })
} }
pub fn to_scim_kanidm(&self) -> Result<ScimEntryKanidm, OperationError> { pub fn to_scim_kanidm(
let attrs = self &self,
mut read_txn: QueryServerReadTransaction,
) -> Result<ScimEntryKanidm, OperationError> {
let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
.attrs .attrs
.iter() .iter()
// We want to skip some attributes as they are already in the header. // We want to skip some attributes as they are already in the header.
.filter(|(k, _vs)| **k != Attribute::Uuid) .filter(|(k, _vs)| **k != Attribute::Uuid)
.filter_map(|(k, vs)| vs.to_scim_value().map(|scim_value| (k.clone(), scim_value))) .filter_map(|(k, vs)| {
let opt_resolve_status = vs.to_scim_value();
let res_opt_scim_value = match opt_resolve_status {
None => Ok(None),
Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
resolve_scim_interim(scim_value_interim, &mut read_txn)
}
};
res_opt_scim_value
.transpose()
.map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
})
.collect(); .collect();
let attrs = result?;
let id = self.get_uuid(); let id = self.get_uuid();
// Not sure how I want to handle this yet, I think we need some schema changes // Not sure how I want to handle this yet, I think we need some schema changes
@ -2353,6 +2370,37 @@ impl Entry<EntryReduced, EntryCommitted> {
} }
} }
fn resolve_scim_interim(
scim_value_intermediate: ScimValueIntermediate,
read_txn: &mut QueryServerReadTransaction,
) -> Result<Option<ScimValueKanidm>, OperationError> {
match scim_value_intermediate {
ScimValueIntermediate::Refer(uuid) => {
if let Some(option) = read_txn.uuid_to_spn(uuid)? {
Ok(Some(ScimValueKanidm::EntryReference(ScimReference {
uuid,
value: option.to_proto_string_clone(),
})))
} else {
// TODO: didn't have spn, fallback to uuid.to_string ?
Ok(None)
}
}
ScimValueIntermediate::ReferMany(uuids) => {
let mut scim_references = vec![];
for uuid in uuids {
if let Some(option) = read_txn.uuid_to_spn(uuid)? {
scim_references.push(ScimReference {
uuid,
value: option.to_proto_string_clone(),
})
}
}
Ok(Some(ScimValueKanidm::EntryReferences(scim_references)))
}
}
}
// impl<STATE> Entry<EntryValid, STATE> { // impl<STATE> Entry<EntryValid, STATE> {
impl<VALID, STATE> Entry<VALID, STATE> { impl<VALID, STATE> Entry<VALID, STATE> {
/// This internally adds an AVA to the entry. If the entry was newly added, then true is returned. /// This internally adds an AVA to the entry. If the entry was newly added, then true is returned.

View file

@ -2268,6 +2268,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*; use crate::prelude::*;
use kanidm_proto::scim_v1::server::ScimReference;
#[qs_test] #[qs_test]
async fn test_name_to_uuid(server: &QueryServer) { async fn test_name_to_uuid(server: &QueryServer) {
@ -2613,4 +2614,66 @@ mod tests {
server_txn.commit().expect("should not fail"); server_txn.commit().expect("should not fail");
// Commit. // Commit.
} }
#[qs_test]
async fn test_scim_entry_structure(server: &QueryServer) {
let mut read_txn = server.read().await.unwrap();
// Query entry (A buitin one ?)
let entry = read_txn
.internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
.unwrap();
// Convert entry into scim
let reduced = entry.as_ref().clone().into_reduced();
let scim_entry = reduced.to_scim_kanidm(read_txn).unwrap();
// Assert scim entry attributes are as expected
assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
match name_scim {
ScimValueKanidm::String(name) => {
assert_eq!(name.clone(), "idm_people_self_name_write")
}
_ => {
panic!("expected String, actual {:?}", name_scim);
}
}
// such as returning a new struct type for `members` attributes or `managed_by`
let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
match entry_managed_by_scim {
ScimValueKanidm::EntryReferences(managed_by) => {
assert_eq!(
managed_by.first().unwrap().clone(),
ScimReference {
uuid: UUID_IDM_ADMINS,
value: "idm_admins@example.com".to_string()
}
)
}
_ => {
panic!(
"expected EntryReference, actual {:?}",
entry_managed_by_scim
);
}
}
let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
match members_scim {
ScimValueKanidm::EntryReferences(members) => {
assert_eq!(
members.first().unwrap().clone(),
ScimReference {
uuid: UUID_IDM_ALL_PERSONS,
value: "idm_all_persons@example.com".to_string()
}
)
}
_ => {
panic!("expected EntryReferences, actual {:?}", members_scim);
}
}
}
} }

View file

@ -7,7 +7,7 @@ use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::value::{Address, VALIDATE_EMAIL_RE}; use crate::value::{Address, VALIDATE_EMAIL_RE};
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use kanidm_proto::scim_v1::server::{ScimAddress, ScimMail}; use kanidm_proto::scim_v1::server::{ScimAddress, ScimMail};
@ -137,8 +137,8 @@ impl ValueSetT for ValueSetAddress {
Box::new(self.set.iter().map(|a| a.formatted.clone())) Box::new(self.set.iter().map(|a| a.formatted.clone()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set self.set
.iter() .iter()
.map(|a| ScimAddress { .map(|a| ScimAddress {
@ -150,7 +150,7 @@ impl ValueSetT for ValueSetAddress {
country: a.country.clone(), country: a.country.clone(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -427,8 +427,8 @@ impl ValueSetT for ValueSetEmailAddress {
} }
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set self.set
.iter() .iter()
.map(|mail| { .map(|mail| {
@ -439,7 +439,7 @@ impl ValueSetT for ValueSetEmailAddress {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -2,6 +2,7 @@ use crate::be::dbvalue::{DbValueApplicationPassword, DbValueSetV2};
use crate::credential::{apppwd::ApplicationPassword, Password}; use crate::credential::{apppwd::ApplicationPassword, Password};
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use kanidm_proto::scim_v1::server::ScimApplicationPassword; use kanidm_proto::scim_v1::server::ScimApplicationPassword;
@ -183,8 +184,8 @@ impl ValueSetT for ValueSetApplicationPassword {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.values() .values()
.flatten() .flatten()
@ -194,7 +195,7 @@ impl ValueSetT for ValueSetApplicationPassword {
label: app_pwd.label.clone(), label: app_pwd.label.clone(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,6 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use kanidm_proto::scim_v1::server::ScimAuditString; use kanidm_proto::scim_v1::server::ScimAuditString;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -109,8 +110,8 @@ impl ValueSetT for ValueSetAuditLogString {
Box::new(self.map.iter().map(|(d, s)| format!("{d}-{s}"))) Box::new(self.map.iter().map(|(d, s)| format!("{d}-{s}")))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(cid, strdata)| { .map(|(cid, strdata)| {
@ -121,7 +122,7 @@ impl ValueSetT for ValueSetAuditLogString {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,3 +1,4 @@
use crate::valueset::ScimResolveStatus;
use base64urlsafedata::Base64UrlSafeData; use base64urlsafedata::Base64UrlSafeData;
use std::collections::btree_map::Entry as BTreeEntry; use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -107,7 +108,7 @@ impl ValueSetT for ValueSetPrivateBinary {
Box::new(self.set.iter().map(|_| "private_binary".to_string())) Box::new(self.set.iter().map(|_| "private_binary".to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }
@ -277,8 +278,8 @@ impl ValueSetT for ValueSetPublicBinary {
Box::new(self.map.keys().cloned()) Box::new(self.map.keys().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(tag, bin)| ScimBinary { .map(|(tag, bin)| ScimBinary {
@ -286,7 +287,7 @@ impl ValueSetT for ValueSetPublicBinary {
value: bin.clone(), value: bin.clone(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetBool { pub struct ValueSetBool {
@ -105,7 +105,7 @@ impl ValueSetT for ValueSetBool {
Box::new(self.set.iter().map(|b| b.to_string())) Box::new(self.set.iter().map(|b| b.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
if self.len() == 1 { if self.len() == 1 {
// Because self.len == 1 we know this has to yield a value. // Because self.len == 1 we know this has to yield a value.
let b = self.set.iter().copied().next().unwrap_or_default(); let b = self.set.iter().copied().next().unwrap_or_default();

View file

@ -1,6 +1,7 @@
use crate::be::dbvalue::DbValueCertificate; use crate::be::dbvalue::DbValueCertificate;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use kanidm_proto::scim_v1::server::ScimCertificate; use kanidm_proto::scim_v1::server::ScimCertificate;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -191,7 +192,7 @@ impl ValueSetT for ValueSetCertificate {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let vals: Vec<ScimCertificate> = self let vals: Vec<ScimCertificate> = self
.map .map
.iter() .iter()
@ -216,7 +217,7 @@ impl ValueSetT for ValueSetCertificate {
if vals.is_empty() { if vals.is_empty() {
None None
} else { } else {
Some(ScimValueKanidm::from(vals)) Some(ScimValueKanidm::from(vals).into())
} }
} }
@ -300,7 +301,7 @@ raBy6edj7W0EIH+yQxkDEwIhAI0nVKaI6duHLAvtKW6CfEQFG6jKg7dyk37YYiRD
let vs: ValueSet = ValueSetCertificate::new(Box::new(cert)).unwrap(); let vs: ValueSet = ValueSetCertificate::new(Box::new(cert)).unwrap();
let scim_value = vs.to_scim_value().unwrap(); let scim_value = vs.to_scim_value().unwrap().assume_resolved();
let cert = match scim_value { let cert = match scim_value {
ScimValueKanidm::ArrayCertificate(mut set) => set.pop().unwrap(), ScimValueKanidm::ArrayCertificate(mut set) => set.pop().unwrap(),

View file

@ -1,10 +1,10 @@
use smolset::SmolSet;
use crate::be::dbvalue::DbCidV1; use crate::be::dbvalue::DbCidV1;
use crate::prelude::*; use crate::prelude::*;
use crate::repl::cid::Cid; use crate::repl::cid::Cid;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetCid { pub struct ValueSetCid {
@ -115,7 +115,7 @@ impl ValueSetT for ValueSetCid {
Box::new(self.set.iter().map(|c| c.to_string())) Box::new(self.set.iter().map(|c| c.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().map(|cid| cid.to_string()); let mut iter = self.set.iter().map(|cid| cid.to_string());
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,3 +1,4 @@
use crate::valueset::ScimResolveStatus;
use smolset::SmolSet; use smolset::SmolSet;
use std::collections::btree_map::Entry as BTreeEntry; use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -141,7 +142,7 @@ impl ValueSetT for ValueSetCredential {
Box::new(self.map.keys().cloned()) Box::new(self.map.keys().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
// Currently I think we don't need to yield cred info as that's part of the // Currently I think we don't need to yield cred info as that's part of the
// cred update session instead. // cred update session instead.
None None
@ -366,8 +367,8 @@ impl ValueSetT for ValueSetIntentToken {
Box::new(self.map.keys().cloned()) Box::new(self.map.keys().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(token_id, intent_token_state)| { .map(|(token_id, intent_token_state)| {
@ -392,7 +393,7 @@ impl ValueSetT for ValueSetIntentToken {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -609,7 +610,7 @@ impl ValueSetT for ValueSetPasskey {
Box::new(self.map.values().map(|(t, _)| t).cloned()) Box::new(self.map.values().map(|(t, _)| t).cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }
@ -782,7 +783,7 @@ impl ValueSetT for ValueSetAttestedPasskey {
Box::new(self.map.values().map(|(t, _)| t).cloned()) Box::new(self.map.values().map(|(t, _)| t).cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }
@ -947,10 +948,10 @@ impl ValueSetT for ValueSetCredentialType {
Box::new(self.set.iter().map(|ct| ct.to_string())) Box::new(self.set.iter().map(|ct| ct.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set.iter().map(|ct| ct.to_string()).collect::<Vec<_>>(), self.set.iter().map(|ct| ct.to_string()).collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -1089,15 +1090,15 @@ impl ValueSetT for ValueSetWebauthnAttestationCaList {
} }
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.ca_list self.ca_list
.cas() .cas()
.values() .values()
.flat_map(|att_ca| att_ca.aaguids().values()) .flat_map(|att_ca| att_ca.aaguids().values())
.map(|device| device.description_en().to_string()) .map(|device| device.description_en().to_string())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> { fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {

View file

@ -1,10 +1,11 @@
use smolset::SmolSet;
use time::OffsetDateTime;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use smolset::SmolSet;
use time::OffsetDateTime;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetDateTime { pub struct ValueSetDateTime {
set: SmolSet<[OffsetDateTime; 1]>, set: SmolSet<[OffsetDateTime; 1]>,
@ -123,7 +124,7 @@ impl ValueSetT for ValueSetDateTime {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().copied(); let mut iter = self.set.iter().copied();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or(OffsetDateTime::UNIX_EPOCH); let v = iter.next().unwrap_or(OffsetDateTime::UNIX_EPOCH);

View file

@ -1,13 +1,14 @@
use crate::valueset::ScimResolveStatus;
use std::iter::{self}; use std::iter::{self};
use super::ValueSet;
use crate::be::dbvalue::DbValueSetV2; use crate::be::dbvalue::DbValueSetV2;
use crate::prelude::*; use crate::prelude::*;
use crate::value::{PartialValue, SyntaxType, Value}; use crate::value::{PartialValue, SyntaxType, Value};
use openssl::ec::EcKey; use openssl::ec::EcKey;
use openssl::pkey::{Private, Public}; use openssl::pkey::{Private, Public};
use super::ValueSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EcKeyPrivate { struct EcKeyPrivate {
priv_key: EcKey<Private>, priv_key: EcKey<Private>,
@ -128,7 +129,7 @@ impl ValueSetT for ValueSetEcKeyPrivate {
Box::new(iter::once(String::from("hidden"))) Box::new(iter::once(String::from("hidden")))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }

View file

@ -1,9 +1,10 @@
use std::collections::BTreeSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use std::collections::BTreeSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetHexString { pub struct ValueSetHexString {
set: BTreeSet<String>, set: BTreeSet<String>,
@ -127,7 +128,7 @@ impl ValueSetT for ValueSetHexString {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,16 +1,16 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::valueset::ScimResolveStatus;
use std::fmt::Display; use std::fmt::Display;
use hashbrown::HashSet;
use image::codecs::gif::GifDecoder;
use image::codecs::webp::WebPDecoder;
use image::ImageDecoder;
use kanidm_proto::internal::{ImageType, ImageValue};
use crate::be::dbvalue::DbValueImage; use crate::be::dbvalue::DbValueImage;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use hashbrown::HashSet;
use image::codecs::gif::GifDecoder;
use image::codecs::webp::WebPDecoder;
use image::ImageDecoder;
use kanidm_proto::internal::{ImageType, ImageValue};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetImage { pub struct ValueSetImage {
@ -388,7 +388,7 @@ impl ValueSetT for ValueSetImage {
Box::new(self.set.iter().map(|image| image.hash_imagevalue())) Box::new(self.set.iter().map(|image| image.hash_imagevalue()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
// TODO: This should be a reference to the image URL, not the image itself! // TODO: This should be a reference to the image URL, not the image itself!
// Does this mean we need to pass in the domain / origin so we can render // Does this mean we need to pass in the domain / origin so we can render
// these URL's correctly? // these URL's correctly?
@ -398,12 +398,12 @@ impl ValueSetT for ValueSetImage {
// //
// TODO: Scim supports a "type" field here, but do we care? // TODO: Scim supports a "type" field here, but do we care?
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set self.set
.iter() .iter()
.map(|image| image.hash_imagevalue()) .map(|image| image.hash_imagevalue())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,10 +1,11 @@
use std::collections::BTreeSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use std::collections::BTreeSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetIname { pub struct ValueSetIname {
set: BTreeSet<String>, set: BTreeSet<String>,
@ -138,7 +139,7 @@ impl ValueSetT for ValueSetIname {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,9 +1,10 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetIndex { pub struct ValueSetIndex {
set: SmolSet<[IndexType; 3]>, set: SmolSet<[IndexType; 3]>,
@ -105,10 +106,10 @@ impl ValueSetT for ValueSetIndex {
Box::new(self.set.iter().map(|b| b.to_string())) Box::new(self.set.iter().map(|b| b.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(), self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,11 +1,12 @@
use std::collections::BTreeSet;
use super::iname::ValueSetIname; use super::iname::ValueSetIname;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use std::collections::BTreeSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetIutf8 { pub struct ValueSetIutf8 {
set: BTreeSet<String>, set: BTreeSet<String>,
@ -136,7 +137,7 @@ impl ValueSetT for ValueSetIutf8 {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,9 +1,10 @@
use kanidm_proto::internal::Filter as ProtoFilter;
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use kanidm_proto::internal::Filter as ProtoFilter;
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetJsonFilter { pub struct ValueSetJsonFilter {
@ -121,8 +122,8 @@ impl ValueSetT for ValueSetJsonFilter {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set self.set
.iter() .iter()
.filter_map(|s| { .filter_map(|s| {
@ -133,7 +134,7 @@ impl ValueSetT for ValueSetJsonFilter {
.ok() .ok()
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,11 +1,11 @@
use crate::prelude::*;
use crate::schema::SchemaAttribute;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet};
use base64urlsafedata::Base64UrlSafeData; use base64urlsafedata::Base64UrlSafeData;
use compact_jwt::{crypto::JwsRs256Signer, JwsEs256Signer, JwsSigner}; use compact_jwt::{crypto::JwsRs256Signer, JwsEs256Signer, JwsSigner};
use hashbrown::HashSet; use hashbrown::HashSet;
use crate::prelude::*;
use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetJwsKeyEs256 { pub struct ValueSetJwsKeyEs256 {
set: HashSet<JwsEs256Signer>, set: HashSet<JwsEs256Signer>,
@ -129,7 +129,7 @@ impl ValueSetT for ValueSetJwsKeyEs256 {
Box::new(self.set.iter().map(|k| k.get_kid().to_string())) Box::new(self.set.iter().map(|k| k.get_kid().to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }
@ -294,7 +294,7 @@ impl ValueSetT for ValueSetJwsKeyRs256 {
Box::new(self.set.iter().map(|k| k.get_kid().to_string())) Box::new(self.set.iter().map(|k| k.get_kid().to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }

View file

@ -1,4 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use crate::valueset::ScimResolveStatus;
use crate::server::keys::KeyId; use crate::server::keys::KeyId;
use crate::value::{KeyStatus, KeyUsage}; use crate::value::{KeyStatus, KeyUsage};
@ -282,8 +283,8 @@ impl ValueSetT for ValueSetKeyInternal {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(kid, key_object)| { .map(|(kid, key_object)| {
@ -298,7 +299,7 @@ impl ValueSetT for ValueSetKeyInternal {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -8,6 +8,8 @@ use kanidm_proto::internal::ImageValue;
use openssl::ec::EcKey; use openssl::ec::EcKey;
use openssl::pkey::Private; use openssl::pkey::Private;
use openssl::pkey::Public; use openssl::pkey::Public;
use serde::Serialize;
use serde_with::serde_as;
use smolset::SmolSet; use smolset::SmolSet;
use sshkey_attest::proto::PublicKey as SshPublicKey; use sshkey_attest::proto::PublicKey as SshPublicKey;
use time::OffsetDateTime; use time::OffsetDateTime;
@ -15,8 +17,6 @@ use webauthn_rs::prelude::AttestationCaList;
use webauthn_rs::prelude::AttestedPasskey as AttestedPasskeyV4; use webauthn_rs::prelude::AttestedPasskey as AttestedPasskeyV4;
use webauthn_rs::prelude::Passkey as PasskeyV4; use webauthn_rs::prelude::Passkey as PasskeyV4;
use kanidm_proto::internal::{Filter as ProtoFilter, UiHint};
use crate::be::dbvalue::DbValueSetV2; use crate::be::dbvalue::DbValueSetV2;
use crate::credential::{apppwd::ApplicationPassword, totp::Totp, Credential}; use crate::credential::{apppwd::ApplicationPassword, totp::Totp, Credential};
use crate::prelude::*; use crate::prelude::*;
@ -24,6 +24,7 @@ use crate::repl::cid::Cid;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::server::keys::KeyId; use crate::server::keys::KeyId;
use crate::value::{Address, ApiToken, CredentialType, IntentTokenState, Oauth2Session, Session}; use crate::value::{Address, ApiToken, CredentialType, IntentTokenState, Oauth2Session, Session};
use kanidm_proto::internal::{Filter as ProtoFilter, UiHint};
pub use self::address::{ValueSetAddress, ValueSetEmailAddress}; pub use self::address::{ValueSetAddress, ValueSetEmailAddress};
use self::apppwd::ValueSetApplicationPassword; use self::apppwd::ValueSetApplicationPassword;
@ -144,7 +145,7 @@ pub trait ValueSetT: std::fmt::Debug + DynClone {
fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_>; fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_>;
fn to_scim_value(&self) -> Option<ScimValueKanidm>; fn to_scim_value(&self) -> Option<ScimResolveStatus>;
fn to_db_valueset_v2(&self) -> DbValueSetV2; fn to_db_valueset_v2(&self) -> DbValueSetV2;
@ -666,6 +667,46 @@ impl PartialEq for ValueSet {
} }
} }
#[serde_as]
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
pub enum ScimValueIntermediate {
Refer(Uuid),
ReferMany(Vec<Uuid>),
}
pub enum ScimResolveStatus {
Resolved(ScimValueKanidm),
NeedsResolution(ScimValueIntermediate),
}
impl<T> From<T> for ScimResolveStatus
where
T: Into<ScimValueKanidm>,
{
fn from(v: T) -> Self {
Self::Resolved(v.into())
}
}
#[cfg(test)]
impl ScimResolveStatus {
pub fn assume_resolved(self) -> ScimValueKanidm {
match self {
ScimResolveStatus::Resolved(v) => v,
ScimResolveStatus::NeedsResolution(_) => {
panic!("assume_resolved called on NeedsResolution")
}
}
}
pub fn assume_unresolved(self) -> ScimValueIntermediate {
match self {
ScimResolveStatus::Resolved(_) => panic!("assume_unresolved called on Resolved"),
ScimResolveStatus::NeedsResolution(svi) => svi,
}
}
}
pub fn uuid_to_proto_string(u: Uuid) -> String { pub fn uuid_to_proto_string(u: Uuid) -> String {
u.as_hyphenated().to_string() u.as_hyphenated().to_string()
} }
@ -877,7 +918,21 @@ pub fn from_db_valueset_v2(dbvs: DbValueSetV2) -> Result<ValueSet, OperationErro
#[cfg(test)] #[cfg(test)]
pub(crate) fn scim_json_reflexive(vs: ValueSet, data: &str) { pub(crate) fn scim_json_reflexive(vs: ValueSet, data: &str) {
let scim_value = vs.to_scim_value().unwrap(); let scim_value = vs.to_scim_value().unwrap().assume_resolved();
let strout = serde_json::to_string_pretty(&scim_value).unwrap();
eprintln!("{}", strout);
let json_value: serde_json::Value = serde_json::to_value(&scim_value).unwrap();
let expect: serde_json::Value = serde_json::from_str(data).unwrap();
assert_eq!(json_value, expect);
}
#[cfg(test)]
pub(crate) fn scim_json_reflexive_unresolved(vs: ValueSet, data: &str) {
let scim_value = vs.to_scim_value().unwrap().assume_unresolved();
let strout = serde_json::to_string_pretty(&scim_value).unwrap(); let strout = serde_json::to_string_pretty(&scim_value).unwrap();
eprintln!("{}", strout); eprintln!("{}", strout);

View file

@ -1,10 +1,11 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::value::NSUNIQUEID_RE; use crate::value::NSUNIQUEID_RE;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetNsUniqueId { pub struct ValueSetNsUniqueId {
set: SmolSet<[String; 1]>, set: SmolSet<[String; 1]>,
@ -106,7 +107,7 @@ impl ValueSetT for ValueSetNsUniqueId {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,3 +1,4 @@
use crate::valueset::ScimResolveStatus;
use std::collections::btree_map::Entry as BTreeEntry; use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
@ -112,8 +113,8 @@ impl ValueSetT for ValueSetOauthScope {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(str_join(&self.set).into()) Some(ScimResolveStatus::Resolved(str_join(&self.set).into()))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -289,8 +290,8 @@ impl ValueSetT for ValueSetOauthScopeMap {
) )
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(uuid, scopes)| { .map(|(uuid, scopes)| {
@ -301,7 +302,7 @@ impl ValueSetT for ValueSetOauthScopeMap {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -620,8 +621,8 @@ impl ValueSetT for ValueSetOauthClaimMap {
})) }))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.flat_map(|(claim_name, mappings)| { .flat_map(|(claim_name, mappings)| {
@ -636,7 +637,7 @@ impl ValueSetT for ValueSetOauthClaimMap {
}) })
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,10 +1,11 @@
use std::collections::BTreeSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::valueset::ScimResolveStatus;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ValueSet};
use std::collections::BTreeSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetRestricted { pub struct ValueSetRestricted {
set: BTreeSet<String>, set: BTreeSet<String>,
@ -138,7 +139,7 @@ impl ValueSetT for ValueSetRestricted {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetSecret { pub struct ValueSetSecret {
@ -96,7 +96,7 @@ impl ValueSetT for ValueSetSecret {
Box::new(self.set.iter().map(|_| "hidden".to_string())) Box::new(self.set.iter().map(|_| "hidden".to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }
@ -145,8 +145,7 @@ impl ValueSetT for ValueSetSecret {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ValueSetSecret; use crate::valueset::{ValueSet, ValueSetSecret};
use crate::prelude::ValueSet;
#[test] #[test]
fn test_scim_secret() { fn test_scim_secret() {

View file

@ -13,7 +13,7 @@ use crate::schema::SchemaAttribute;
use crate::value::{ use crate::value::{
ApiToken, ApiTokenScope, AuthType, Oauth2Session, Session, SessionScope, SessionState, ApiToken, ApiTokenScope, AuthType, Oauth2Session, Session, SessionScope, SessionState,
}; };
use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ValueSet}; use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ScimResolveStatus, ValueSet};
use kanidm_proto::scim_v1::server::ScimApiToken; use kanidm_proto::scim_v1::server::ScimApiToken;
use kanidm_proto::scim_v1::server::ScimAuthSession; use kanidm_proto::scim_v1::server::ScimAuthSession;
@ -354,8 +354,8 @@ impl ValueSetT for ValueSetSession {
) )
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(session_id, session)| { .map(|(session_id, session)| {
@ -381,7 +381,7 @@ impl ValueSetT for ValueSetSession {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -855,8 +855,8 @@ impl ValueSetT for ValueSetOauth2Session {
) )
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(session_id, session)| { .map(|(session_id, session)| {
@ -879,7 +879,7 @@ impl ValueSetT for ValueSetOauth2Session {
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -1196,8 +1196,8 @@ impl ValueSetT for ValueSetApiToken {
) )
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(token_id, token)| ScimApiToken { .map(|(token_id, token)| ScimApiToken {
@ -1209,7 +1209,7 @@ impl ValueSetT for ValueSetApiToken {
scope: token.scope.to_string(), scope: token.scope.to_string(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetSpn { pub struct ValueSetSpn {
@ -110,7 +110,7 @@ impl ValueSetT for ValueSetSpn {
Box::new(self.set.iter().map(|(n, d)| format!("{n}@{d}"))) Box::new(self.set.iter().map(|(n, d)| format!("{n}@{d}")))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().map(|(n, d)| format!("{n}@{d}")); let mut iter = self.set.iter().map(|(n, d)| format!("{n}@{d}"));
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -5,7 +5,7 @@ use crate::be::dbvalue::DbValueTaggedStringV1;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use sshkey_attest::proto::PublicKey as SshPublicKey; use sshkey_attest::proto::PublicKey as SshPublicKey;
@ -137,8 +137,8 @@ impl ValueSetT for ValueSetSshKey {
Box::new(self.map.iter().map(|(tag, pk)| format!("{}: {}", tag, pk))) Box::new(self.map.iter().map(|(tag, pk)| format!("{}: {}", tag, pk)))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.map self.map
.iter() .iter()
.map(|(label, value)| ScimSshPublicKey { .map(|(label, value)| ScimSshPublicKey {
@ -146,7 +146,7 @@ impl ValueSetT for ValueSetSshKey {
value: value.clone(), value: value.clone(),
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetSyntax { pub struct ValueSetSyntax {
@ -105,10 +105,10 @@ impl ValueSetT for ValueSetSyntax {
Box::new(self.set.iter().map(|b| b.to_string())) Box::new(self.set.iter().map(|b| b.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(), self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,12 +1,12 @@
use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::BTreeMap;
use crate::credential::totp::Totp; use crate::credential::totp::Totp;
use crate::prelude::*; use crate::prelude::*;
use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::BTreeMap;
use crate::be::dbvalue::DbTotpV1; use crate::be::dbvalue::DbTotpV1;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetTotpSecret { pub struct ValueSetTotpSecret {
@ -117,7 +117,7 @@ impl ValueSetT for ValueSetTotpSecret {
Box::new(self.map.keys().cloned()) Box::new(self.map.keys().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
None None
} }

View file

@ -2,7 +2,7 @@ use std::collections::BTreeSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use kanidm_proto::internal::UiHint; use kanidm_proto::internal::UiHint;
@ -94,10 +94,10 @@ impl ValueSetT for ValueSetUiHint {
Box::new(self.set.iter().map(|u| u.to_string())) Box::new(self.set.iter().map(|u| u.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(ScimValueKanidm::from( Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(), self.set.iter().map(|u| u.to_string()).collect::<Vec<_>>(),
)) )))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetUint32 { pub struct ValueSetUint32 {
@ -108,7 +108,7 @@ impl ValueSetT for ValueSetUint32 {
Box::new(self.set.iter().map(|b| b.to_string())) Box::new(self.set.iter().map(|b| b.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
if self.len() == 1 { if self.len() == 1 {
// Because self.len == 1 we know this has to yield a value. // Because self.len == 1 we know this has to yield a value.
let b = self.set.iter().copied().next().unwrap_or_default(); let b = self.set.iter().copied().next().unwrap_or_default();

View file

@ -1,8 +1,8 @@
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetUrl { pub struct ValueSetUrl {
@ -102,7 +102,7 @@ impl ValueSetT for ValueSetUrl {
Box::new(self.set.iter().map(|i| i.to_string())) Box::new(self.set.iter().map(|i| i.to_string()))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().map(|url| url.to_string()); let mut iter = self.set.iter().map(|url| url.to_string());
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,9 +1,9 @@
use std::collections::BTreeSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::utils::trigraph_iter; use crate::utils::trigraph_iter;
use crate::valueset::{DbValueSetV2, ValueSet}; use crate::valueset::{DbValueSetV2, ScimResolveStatus, ValueSet};
use std::collections::BTreeSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetUtf8 { pub struct ValueSetUtf8 {
@ -140,7 +140,7 @@ impl ValueSetT for ValueSetUtf8 {
Box::new(self.set.iter().cloned()) Box::new(self.set.iter().cloned())
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
let mut iter = self.set.iter().cloned(); let mut iter = self.set.iter().cloned();
if self.len() == 1 { if self.len() == 1 {
let v = iter.next().unwrap_or_default(); let v = iter.next().unwrap_or_default();

View file

@ -1,10 +1,11 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use smolset::SmolSet;
use crate::prelude::*; use crate::prelude::*;
use crate::schema::SchemaAttribute; use crate::schema::SchemaAttribute;
use crate::valueset::{uuid_to_proto_string, DbValueSetV2, ValueSet}; use crate::valueset::{
uuid_to_proto_string, DbValueSetV2, ScimResolveStatus, ScimValueIntermediate, ValueSet,
};
use smolset::SmolSet;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ValueSetUuid { pub struct ValueSetUuid {
@ -113,8 +114,12 @@ impl ValueSetT for ValueSetUuid {
Box::new(self.set.iter().copied().map(uuid_to_proto_string)) Box::new(self.set.iter().copied().map(uuid_to_proto_string))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
self.set.iter().next().copied().map(ScimValueKanidm::Uuid) self.set
.iter()
.next()
.copied()
.map(|uuid| ScimResolveStatus::NeedsResolution(ScimValueIntermediate::Refer(uuid)))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -282,8 +287,11 @@ impl ValueSetT for ValueSetRefer {
Box::new(self.set.iter().copied().map(uuid_to_proto_string)) Box::new(self.set.iter().copied().map(uuid_to_proto_string))
} }
fn to_scim_value(&self) -> Option<ScimValueKanidm> { fn to_scim_value(&self) -> Option<ScimResolveStatus> {
Some(self.set.iter().copied().collect::<Vec<_>>().into()) let uuids = self.set.iter().copied().collect::<Vec<_>>();
Some(ScimResolveStatus::NeedsResolution(
ScimValueIntermediate::ReferMany(uuids),
))
} }
fn to_db_valueset_v2(&self) -> DbValueSetV2 { fn to_db_valueset_v2(&self) -> DbValueSetV2 {
@ -346,17 +354,17 @@ mod tests {
fn test_scim_uuid() { fn test_scim_uuid() {
let vs: ValueSet = ValueSetUuid::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f")); let vs: ValueSet = ValueSetUuid::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f"));
let data = r#""4d21d04a-dc0e-42eb-b850-34dd180b107f""#; let data = r#"{"Refer": "4d21d04a-dc0e-42eb-b850-34dd180b107f"}"#;
crate::valueset::scim_json_reflexive(vs, data); crate::valueset::scim_json_reflexive_unresolved(vs, data);
} }
#[test] #[test]
fn test_scim_refer() { fn test_scim_refer() {
let vs: ValueSet = ValueSetRefer::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f")); let vs: ValueSet = ValueSetRefer::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f"));
let data = r#"["4d21d04a-dc0e-42eb-b850-34dd180b107f"]"#; let data = r#"{"ReferMany": ["4d21d04a-dc0e-42eb-b850-34dd180b107f"]}"#;
crate::valueset::scim_json_reflexive(vs, data); crate::valueset::scim_json_reflexive_unresolved(vs, data);
} }
} }