2021-06-02 01:42:40 +02:00
|
|
|
//! Modification expressions and validation. This is how `ModifyEvents` store and
|
|
|
|
//! express the series of Modifications that should be applied. These are expressed
|
|
|
|
//! as "states" on what attribute-values should appear as within the `Entry`
|
|
|
|
|
2022-10-01 08:08:51 +02:00
|
|
|
use std::slice;
|
2019-03-12 06:20:08 +01:00
|
|
|
|
2022-10-01 08:08:51 +02:00
|
|
|
use kanidm_proto::v1::{
|
|
|
|
Entry as ProtoEntry, Modify as ProtoModify, ModifyList as ProtoModifyList, OperationError,
|
|
|
|
SchemaError,
|
|
|
|
};
|
2019-03-12 06:20:08 +01:00
|
|
|
// Should this be std?
|
2021-12-31 00:11:20 +01:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-12-30 00:53:19 +01:00
|
|
|
use smartstring::alias::String as AttrString;
|
2022-10-01 08:08:51 +02:00
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
use crate::schema::SchemaTransaction;
|
|
|
|
use crate::value::{PartialValue, Value};
|
2019-03-12 06:20:08 +01:00
|
|
|
|
2022-07-07 05:28:36 +02:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
2019-03-12 06:20:08 +01:00
|
|
|
pub struct ModifyValid;
|
2022-07-07 05:28:36 +02:00
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
2019-03-12 06:20:08 +01:00
|
|
|
pub struct ModifyInvalid;
|
|
|
|
|
2022-07-07 05:28:36 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2020-06-18 02:30:42 +02:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2019-01-31 05:29:14 +01:00
|
|
|
pub enum Modify {
|
|
|
|
// This value *should* exist.
|
2020-06-18 02:30:42 +02:00
|
|
|
// Clippy doesn't like value here, as value > pv. It could be an improvement to
|
|
|
|
// box here, but not sure. ... TODO and thought needed.
|
2020-12-30 00:53:19 +01:00
|
|
|
Present(AttrString, Value),
|
2019-01-31 05:29:14 +01:00
|
|
|
// This value *should not* exist.
|
2020-12-30 00:53:19 +01:00
|
|
|
Removed(AttrString, PartialValue),
|
2019-01-31 05:29:14 +01:00
|
|
|
// This attr *should not* exist.
|
2020-12-30 00:53:19 +01:00
|
|
|
Purged(AttrString),
|
2022-12-15 07:09:09 +01:00
|
|
|
// This attr and value must exist *in this state* for this change to proceed.
|
|
|
|
Assert(AttrString, PartialValue),
|
2019-01-31 05:29:14 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 11:19:09 +02:00
|
|
|
#[allow(dead_code)]
|
2019-08-27 01:36:54 +02:00
|
|
|
pub fn m_pres(a: &str, v: &Value) -> Modify {
|
2020-12-30 00:53:19 +01:00
|
|
|
Modify::Present(a.into(), v.clone())
|
2019-06-07 11:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2019-08-27 01:36:54 +02:00
|
|
|
pub fn m_remove(a: &str, v: &PartialValue) -> Modify {
|
2020-12-30 00:53:19 +01:00
|
|
|
Modify::Removed(a.into(), v.clone())
|
2019-06-07 11:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub fn m_purge(a: &str) -> Modify {
|
2020-12-30 00:53:19 +01:00
|
|
|
Modify::Purged(AttrString::from(a))
|
2019-06-07 11:19:09 +02:00
|
|
|
}
|
|
|
|
|
2022-12-15 07:09:09 +01:00
|
|
|
#[allow(dead_code)]
|
|
|
|
pub fn m_assert(a: &str, v: &PartialValue) -> Modify {
|
|
|
|
Modify::Assert(a.into(), v.clone())
|
|
|
|
}
|
|
|
|
|
2019-02-22 07:15:48 +01:00
|
|
|
impl Modify {
|
2021-09-21 04:42:00 +02:00
|
|
|
pub fn from(m: &ProtoModify, qs: &QueryServerWriteTransaction) -> Result<Self, OperationError> {
|
2019-03-17 04:24:06 +01:00
|
|
|
Ok(match m {
|
2021-09-21 04:42:00 +02:00
|
|
|
ProtoModify::Present(a, v) => Modify::Present(a.into(), qs.clone_value(a, v)?),
|
|
|
|
ProtoModify::Removed(a, v) => Modify::Removed(a.into(), qs.clone_partialvalue(a, v)?),
|
2020-12-30 00:53:19 +01:00
|
|
|
ProtoModify::Purged(a) => Modify::Purged(a.into()),
|
2019-03-17 04:24:06 +01:00
|
|
|
})
|
2019-02-22 07:15:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-07 05:28:36 +02:00
|
|
|
#[derive(Clone, Debug, Default)]
|
2019-03-12 06:20:08 +01:00
|
|
|
pub struct ModifyList<VALID> {
|
2021-12-16 01:13:03 +01:00
|
|
|
// This is never read, it's just used for state machine enforcement.
|
|
|
|
#[allow(dead_code)]
|
2019-03-12 06:20:08 +01:00
|
|
|
valid: VALID,
|
|
|
|
// The order of this list matters. Each change must be done in order.
|
|
|
|
mods: Vec<Modify>,
|
2019-01-31 05:29:14 +01:00
|
|
|
}
|
|
|
|
|
2019-03-12 06:20:08 +01:00
|
|
|
impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
|
|
|
|
type IntoIter = slice::Iter<'a, Modify>;
|
2022-10-01 08:08:51 +02:00
|
|
|
type Item = &'a Modify;
|
2019-03-12 06:20:08 +01:00
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
self.mods.iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ModifyList<ModifyInvalid> {
|
2019-01-31 05:29:14 +01:00
|
|
|
pub fn new() -> Self {
|
2019-03-12 06:20:08 +01:00
|
|
|
ModifyList {
|
|
|
|
valid: ModifyInvalid,
|
2019-03-12 06:40:25 +01:00
|
|
|
mods: Vec::new(),
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
2019-01-31 05:29:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_list(mods: Vec<Modify>) -> Self {
|
2019-03-12 06:20:08 +01:00
|
|
|
ModifyList {
|
|
|
|
valid: ModifyInvalid,
|
2020-01-09 11:07:14 +01:00
|
|
|
mods,
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
2019-01-31 05:29:14 +01:00
|
|
|
}
|
|
|
|
|
2019-09-04 03:06:37 +02:00
|
|
|
pub fn new_purge_and_set(attr: &str, v: Value) -> Self {
|
2020-12-30 00:53:19 +01:00
|
|
|
Self::new_list(vec![
|
|
|
|
m_purge(attr),
|
|
|
|
Modify::Present(AttrString::from(attr), v),
|
|
|
|
])
|
2019-09-04 03:06:37 +02:00
|
|
|
}
|
|
|
|
|
2019-11-16 05:40:45 +01:00
|
|
|
pub fn new_append(attr: &str, v: Value) -> Self {
|
2020-12-30 00:53:19 +01:00
|
|
|
Self::new_list(vec![Modify::Present(AttrString::from(attr), v)])
|
2019-11-16 05:40:45 +01:00
|
|
|
}
|
|
|
|
|
2021-10-07 10:31:48 +02:00
|
|
|
pub fn new_remove(attr: &str, pv: PartialValue) -> Self {
|
|
|
|
Self::new_list(vec![Modify::Removed(AttrString::from(attr), pv)])
|
|
|
|
}
|
|
|
|
|
2019-10-31 01:48:15 +01:00
|
|
|
pub fn new_purge(attr: &str) -> Self {
|
|
|
|
Self::new_list(vec![m_purge(attr)])
|
|
|
|
}
|
|
|
|
|
2019-01-31 05:29:14 +01:00
|
|
|
pub fn push_mod(&mut self, modify: Modify) {
|
|
|
|
self.mods.push(modify)
|
|
|
|
}
|
2019-02-11 10:49:15 +01:00
|
|
|
|
2019-03-17 04:24:06 +01:00
|
|
|
pub fn from(
|
|
|
|
ml: &ProtoModifyList,
|
2020-06-24 13:17:46 +02:00
|
|
|
qs: &QueryServerWriteTransaction,
|
2019-03-17 04:24:06 +01:00
|
|
|
) -> Result<Self, OperationError> {
|
2019-02-22 07:15:48 +01:00
|
|
|
// For each ProtoModify, do a from.
|
2021-09-21 04:42:00 +02:00
|
|
|
let inner: Result<Vec<_>, _> = ml.mods.iter().map(|pm| Modify::from(pm, qs)).collect();
|
2019-03-17 04:24:06 +01:00
|
|
|
match inner {
|
|
|
|
Ok(m) => Ok(ModifyList {
|
|
|
|
valid: ModifyInvalid,
|
|
|
|
mods: m,
|
|
|
|
}),
|
|
|
|
Err(e) => Err(e),
|
2019-02-22 07:15:48 +01:00
|
|
|
}
|
|
|
|
}
|
2019-03-12 06:20:08 +01:00
|
|
|
|
2021-06-29 06:23:39 +02:00
|
|
|
pub fn from_patch(
|
|
|
|
pe: &ProtoEntry,
|
|
|
|
qs: &QueryServerWriteTransaction,
|
|
|
|
) -> Result<Self, OperationError> {
|
|
|
|
let mut mods = Vec::new();
|
|
|
|
|
|
|
|
pe.attrs.iter().try_for_each(|(attr, vals)| {
|
|
|
|
// Issue a purge to the attr.
|
2021-09-15 00:24:37 +02:00
|
|
|
mods.push(m_purge(attr));
|
2021-06-29 06:23:39 +02:00
|
|
|
// Now if there are vals, push those too.
|
|
|
|
// For each value we want to now be present.
|
|
|
|
vals.iter().try_for_each(|val| {
|
2021-09-21 04:42:00 +02:00
|
|
|
qs.clone_value(attr, val).map(|resolved_v| {
|
2021-06-29 06:23:39 +02:00
|
|
|
mods.push(Modify::Present(attr.as_str().into(), resolved_v));
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
Ok(ModifyList {
|
|
|
|
valid: ModifyInvalid,
|
|
|
|
mods,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-03-12 06:40:25 +01:00
|
|
|
pub fn validate(
|
|
|
|
&self,
|
2019-09-06 05:05:27 +02:00
|
|
|
schema: &dyn SchemaTransaction,
|
2019-03-12 06:40:25 +01:00
|
|
|
) -> Result<ModifyList<ModifyValid>, SchemaError> {
|
2019-03-12 06:40:19 +01:00
|
|
|
let schema_attributes = schema.get_attributes();
|
2019-08-27 01:36:54 +02:00
|
|
|
/*
|
2019-03-12 06:40:19 +01:00
|
|
|
let schema_name = schema_attributes
|
|
|
|
.get("name")
|
|
|
|
.expect("Critical: Core schema corrupt or missing. To initiate a core transfer, please deposit substitute core in receptacle.");
|
2019-08-27 01:36:54 +02:00
|
|
|
*/
|
2019-03-12 06:40:19 +01:00
|
|
|
|
2022-10-29 11:07:54 +02:00
|
|
|
let res: Result<Vec<Modify>, _> = self
|
|
|
|
.mods
|
2020-01-09 11:07:14 +01:00
|
|
|
.iter()
|
2019-03-12 06:40:25 +01:00
|
|
|
.map(|m| match m {
|
|
|
|
Modify::Present(attr, value) => {
|
2019-08-27 01:36:54 +02:00
|
|
|
let attr_norm = schema.normalise_attr_name(attr);
|
2019-03-12 06:40:25 +01:00
|
|
|
match schema_attributes.get(&attr_norm) {
|
2019-08-27 01:36:54 +02:00
|
|
|
Some(schema_a) => schema_a
|
2021-09-15 00:24:37 +02:00
|
|
|
.validate_value(attr_norm.as_str(), value)
|
2019-08-27 01:36:54 +02:00
|
|
|
.map(|_| Modify::Present(attr_norm, value.clone())),
|
2020-12-30 00:53:19 +01:00
|
|
|
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
2019-03-12 06:40:19 +01:00
|
|
|
}
|
2019-03-12 06:40:25 +01:00
|
|
|
}
|
|
|
|
Modify::Removed(attr, value) => {
|
2019-08-27 01:36:54 +02:00
|
|
|
let attr_norm = schema.normalise_attr_name(attr);
|
2019-03-12 06:40:25 +01:00
|
|
|
match schema_attributes.get(&attr_norm) {
|
2019-08-27 01:36:54 +02:00
|
|
|
Some(schema_a) => schema_a
|
2021-09-15 00:24:37 +02:00
|
|
|
.validate_partialvalue(attr_norm.as_str(), value)
|
2019-08-27 01:36:54 +02:00
|
|
|
.map(|_| Modify::Removed(attr_norm, value.clone())),
|
2020-12-30 00:53:19 +01:00
|
|
|
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
2019-03-12 06:40:19 +01:00
|
|
|
}
|
2019-03-12 06:40:25 +01:00
|
|
|
}
|
2022-12-15 07:09:09 +01:00
|
|
|
Modify::Assert(attr, value) => {
|
|
|
|
let attr_norm = schema.normalise_attr_name(attr);
|
|
|
|
match schema_attributes.get(&attr_norm) {
|
|
|
|
Some(schema_a) => schema_a
|
|
|
|
.validate_partialvalue(attr_norm.as_str(), value)
|
|
|
|
.map(|_| Modify::Assert(attr_norm, value.clone())),
|
|
|
|
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 06:40:25 +01:00
|
|
|
Modify::Purged(attr) => {
|
2019-08-27 01:36:54 +02:00
|
|
|
let attr_norm = schema.normalise_attr_name(attr);
|
2019-03-12 06:40:25 +01:00
|
|
|
match schema_attributes.get(&attr_norm) {
|
|
|
|
Some(_attr_name) => Ok(Modify::Purged(attr_norm)),
|
2020-12-30 00:53:19 +01:00
|
|
|
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
2019-03-12 06:40:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let valid_mods = match res {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
};
|
2019-03-12 06:20:08 +01:00
|
|
|
|
|
|
|
// Return new ModifyList!
|
2019-03-12 06:40:19 +01:00
|
|
|
Ok(ModifyList {
|
|
|
|
valid: ModifyValid,
|
|
|
|
mods: valid_mods,
|
|
|
|
})
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
2019-06-07 11:19:09 +02:00
|
|
|
|
2022-09-11 04:23:57 +02:00
|
|
|
#[cfg(test)]
|
2022-07-07 05:28:36 +02:00
|
|
|
pub(crate) unsafe fn into_valid(self) -> ModifyList<ModifyValid> {
|
2019-06-07 11:19:09 +02:00
|
|
|
ModifyList {
|
|
|
|
valid: ModifyValid,
|
|
|
|
mods: self.mods,
|
|
|
|
}
|
|
|
|
}
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ModifyList<ModifyValid> {
|
|
|
|
#[cfg(test)]
|
|
|
|
pub unsafe fn new_valid_list(mods: Vec<Modify>) -> Self {
|
|
|
|
ModifyList {
|
|
|
|
valid: ModifyValid,
|
2020-01-09 11:07:14 +01:00
|
|
|
mods,
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-07 11:19:09 +02:00
|
|
|
|
|
|
|
pub fn iter(&self) -> slice::Iter<Modify> {
|
|
|
|
self.mods.iter()
|
|
|
|
}
|
2019-03-12 06:20:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<VALID> ModifyList<VALID> {
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.mods.len()
|
|
|
|
}
|
2022-02-20 03:43:38 +01:00
|
|
|
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.len() == 0
|
|
|
|
}
|
2019-01-31 05:29:14 +01:00
|
|
|
}
|