mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
20190508 UUID on entry (#50)
* Make UUID a proper type on entries * Add auth and ID data to relevant structures - this means we can start access controls!
This commit is contained in:
parent
9eca06c3e2
commit
44dc66713c
|
@ -806,6 +806,10 @@ mod tests {
|
|||
|
||||
let mut e: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
e.add_ava(
|
||||
"uuid".to_string(),
|
||||
"db237e8a-0079-4b8c-8a56-593b22aa44d1".to_string(),
|
||||
);
|
||||
let e = unsafe { e.to_valid_new() };
|
||||
|
||||
let single_result = be.create(audit, &vec![e.clone()]);
|
||||
|
@ -824,6 +828,10 @@ mod tests {
|
|||
|
||||
let mut e: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e.add_ava(String::from("userid"), String::from("claire"));
|
||||
e.add_ava(
|
||||
"uuid".to_string(),
|
||||
"db237e8a-0079-4b8c-8a56-593b22aa44d1".to_string(),
|
||||
);
|
||||
let e = unsafe { e.to_valid_new() };
|
||||
|
||||
let single_result = be.create(audit, &vec![e.clone()]);
|
||||
|
@ -850,9 +858,17 @@ mod tests {
|
|||
// First create some entries (3?)
|
||||
let mut e1: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e1.add_ava(String::from("userid"), String::from("william"));
|
||||
e1.add_ava(
|
||||
"uuid".to_string(),
|
||||
"db237e8a-0079-4b8c-8a56-593b22aa44d1".to_string(),
|
||||
);
|
||||
|
||||
let mut e2: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e2.add_ava(String::from("userid"), String::from("alice"));
|
||||
e2.add_ava(
|
||||
"uuid".to_string(),
|
||||
"4b6228ab-1dbe-42a4-a9f5-f6368222438e".to_string(),
|
||||
);
|
||||
|
||||
let ve1 = unsafe { e1.clone().to_valid_new() };
|
||||
let ve2 = unsafe { e2.clone().to_valid_new() };
|
||||
|
@ -912,12 +928,24 @@ mod tests {
|
|||
// First create some entries (3?)
|
||||
let mut e1: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e1.add_ava(String::from("userid"), String::from("william"));
|
||||
e1.add_ava(
|
||||
"uuid".to_string(),
|
||||
"db237e8a-0079-4b8c-8a56-593b22aa44d1".to_string(),
|
||||
);
|
||||
|
||||
let mut e2: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e2.add_ava(String::from("userid"), String::from("alice"));
|
||||
e2.add_ava(
|
||||
"uuid".to_string(),
|
||||
"4b6228ab-1dbe-42a4-a9f5-f6368222438e".to_string(),
|
||||
);
|
||||
|
||||
let mut e3: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e3.add_ava(String::from("userid"), String::from("lucy"));
|
||||
e3.add_ava(
|
||||
"uuid".to_string(),
|
||||
"7b23c99d-c06b-4a9a-a958-3afa56383e1d".to_string(),
|
||||
);
|
||||
|
||||
let ve1 = unsafe { e1.clone().to_valid_new() };
|
||||
let ve2 = unsafe { e2.clone().to_valid_new() };
|
||||
|
@ -950,6 +978,10 @@ mod tests {
|
|||
// the state machine rules here!!!!
|
||||
let mut e4: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e4.add_ava(String::from("userid"), String::from("amy"));
|
||||
e4.add_ava(
|
||||
"uuid".to_string(),
|
||||
"21d816b5-1f6a-4696-b7c1-6ed06d22ed81".to_string(),
|
||||
);
|
||||
|
||||
let ve4 = unsafe { e4.clone().to_valid_committed() };
|
||||
|
||||
|
@ -978,12 +1010,24 @@ mod tests {
|
|||
// First create some entries (3?)
|
||||
let mut e1: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e1.add_ava(String::from("userid"), String::from("william"));
|
||||
e1.add_ava(
|
||||
"uuid".to_string(),
|
||||
"db237e8a-0079-4b8c-8a56-593b22aa44d1".to_string(),
|
||||
);
|
||||
|
||||
let mut e2: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e2.add_ava(String::from("userid"), String::from("alice"));
|
||||
e2.add_ava(
|
||||
"uuid".to_string(),
|
||||
"4b6228ab-1dbe-42a4-a9f5-f6368222438e".to_string(),
|
||||
);
|
||||
|
||||
let mut e3: Entry<EntryInvalid, EntryNew> = Entry::new();
|
||||
e3.add_ava(String::from("userid"), String::from("lucy"));
|
||||
e3.add_ava(
|
||||
"uuid".to_string(),
|
||||
"7b23c99d-c06b-4a9a-a958-3afa56383e1d".to_string(),
|
||||
);
|
||||
|
||||
let ve1 = unsafe { e1.clone().to_valid_new() };
|
||||
let ve2 = unsafe { e2.clone().to_valid_new() };
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
pub static PURGE_TIMEOUT: u64 = 3600;
|
||||
|
||||
pub static _UUID_ADMIN: &'static str = "00000000-0000-0000-0000-000000000000";
|
||||
pub static UUID_ADMIN: &'static str = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
pub static _UUID_ANONYMOUS: &'static str = "00000000-0000-0000-0000-ffffffffffff";
|
||||
pub static JSON_ANONYMOUS_V1: &'static str = r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "00000000-0000-0000-0000-ffffffffffff"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["account", "object"],
|
||||
|
@ -18,7 +20,9 @@ pub static JSON_ANONYMOUS_V1: &'static str = r#"{
|
|||
|
||||
pub static _UUID_SYSTEM_INFO: &'static str = "00000000-0000-0000-0000-ffffff000001";
|
||||
pub static JSON_SYSTEM_INFO_V1: &'static str = r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "00000000-0000-0000-0000-ffffff000001"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "system_info"],
|
||||
|
|
123
src/lib/entry.rs
123
src/lib/entry.rs
|
@ -131,10 +131,16 @@ pub struct EntryCommitted {
|
|||
} // It's been in the DB, so it has an id
|
||||
// pub struct EntryPurged;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct EntryValid {
|
||||
// Asserted with schema, so we know it has a UUID now ...
|
||||
uuid: String,
|
||||
}
|
||||
|
||||
// Modified, can't be sure of it's content! We therefore disregard the UUID
|
||||
// and on validate, we check it again.
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
pub struct EntryValid; // Asserted with schema.
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
pub struct EntryInvalid; // Modified
|
||||
pub struct EntryInvalid;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Entry<VALID, STATE> {
|
||||
|
@ -205,6 +211,13 @@ impl Entry<EntryInvalid, EntryNew> {
|
|||
}
|
||||
|
||||
impl<STATE> Entry<EntryInvalid, STATE> {
|
||||
fn get_uuid(&self) -> Option<&String> {
|
||||
match self.attrs.get("uuid") {
|
||||
Some(vs) => vs.first(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(
|
||||
self,
|
||||
schema: &SchemaReadTransaction,
|
||||
|
@ -230,7 +243,7 @@ impl<STATE> Entry<EntryInvalid, STATE> {
|
|||
|
||||
let mut new_attrs = BTreeMap::new();
|
||||
|
||||
// First normalise
|
||||
// First normalise - this checks and fixes our UUID.
|
||||
for (attr_name, avas) in attrs.iter() {
|
||||
let attr_name_normal: String = schema_attr_name.normalise_value(attr_name);
|
||||
// Get the needed schema type
|
||||
|
@ -255,8 +268,16 @@ impl<STATE> Entry<EntryInvalid, STATE> {
|
|||
let _ = new_attrs.insert(attr_name_normal, avas_normal);
|
||||
}
|
||||
|
||||
let uuid: String = match new_attrs.get("uuid") {
|
||||
Some(vs) => match vs.first() {
|
||||
Some(uuid) => uuid.to_string(),
|
||||
None => return Err(SchemaError::MissingMustAttribute("uuid".to_string())),
|
||||
},
|
||||
None => return Err(SchemaError::MissingMustAttribute("uuid".to_string())),
|
||||
};
|
||||
|
||||
let ne = Entry {
|
||||
valid: EntryValid,
|
||||
valid: EntryValid { uuid },
|
||||
state: state,
|
||||
attrs: new_attrs,
|
||||
};
|
||||
|
@ -359,13 +380,13 @@ impl<STATE> Entry<EntryInvalid, STATE> {
|
|||
|
||||
impl<VALID, STATE> Clone for Entry<VALID, STATE>
|
||||
where
|
||||
VALID: Copy,
|
||||
VALID: Clone,
|
||||
STATE: Copy,
|
||||
{
|
||||
// Dirty modifiable state. Works on any other state to dirty them.
|
||||
fn clone(&self) -> Entry<VALID, STATE> {
|
||||
Entry {
|
||||
valid: self.valid,
|
||||
valid: self.valid.clone(),
|
||||
state: self.state,
|
||||
attrs: self.attrs.clone(),
|
||||
}
|
||||
|
@ -376,11 +397,13 @@ where
|
|||
* A series of unsafe transitions allowing entries to skip certain steps in
|
||||
* the process to facilitate eq/checks.
|
||||
*/
|
||||
impl<VALID, STATE> Entry<VALID, STATE> {
|
||||
impl Entry<EntryInvalid, EntryCommitted> {
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_new(self) -> Entry<EntryValid, EntryNew> {
|
||||
Entry {
|
||||
valid: EntryValid,
|
||||
valid: EntryValid {
|
||||
uuid: self.get_uuid().expect("Invalid uuid").to_string(),
|
||||
},
|
||||
state: EntryNew,
|
||||
attrs: self.attrs,
|
||||
}
|
||||
|
@ -388,22 +411,37 @@ impl<VALID, STATE> Entry<VALID, STATE> {
|
|||
}
|
||||
// Both invalid states can be reached from "entry -> invalidate"
|
||||
|
||||
impl<VALID> Entry<VALID, EntryNew> {
|
||||
impl Entry<EntryInvalid, EntryNew> {
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_new(self) -> Entry<EntryValid, EntryNew> {
|
||||
Entry {
|
||||
valid: EntryValid {
|
||||
uuid: self.get_uuid().expect("Invalid uuid").to_string(),
|
||||
},
|
||||
state: EntryNew,
|
||||
attrs: self.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
|
||||
Entry {
|
||||
valid: EntryValid,
|
||||
valid: EntryValid {
|
||||
uuid: self.get_uuid().expect("Invalid uuid").to_string(),
|
||||
},
|
||||
state: EntryCommitted { id: 0 },
|
||||
attrs: self.attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VALID> Entry<VALID, EntryCommitted> {
|
||||
impl Entry<EntryInvalid, EntryCommitted> {
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
|
||||
Entry {
|
||||
valid: EntryValid,
|
||||
valid: EntryValid {
|
||||
uuid: self.get_uuid().expect("Invalid uuid").to_string(),
|
||||
},
|
||||
state: self.state,
|
||||
attrs: self.attrs,
|
||||
}
|
||||
|
@ -411,30 +449,42 @@ impl<VALID> Entry<VALID, EntryCommitted> {
|
|||
}
|
||||
|
||||
impl Entry<EntryValid, EntryNew> {
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
|
||||
Entry {
|
||||
valid: self.valid,
|
||||
state: EntryCommitted { id: 0 },
|
||||
attrs: self.attrs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare(&self, rhs: &Entry<EntryValid, EntryCommitted>) -> bool {
|
||||
self.attrs == rhs.attrs
|
||||
}
|
||||
}
|
||||
|
||||
impl Entry<EntryValid, EntryCommitted> {
|
||||
#[cfg(test)]
|
||||
pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
|
||||
// NO-OP to satisfy macros.
|
||||
self
|
||||
}
|
||||
|
||||
pub fn compare(&self, rhs: &Entry<EntryValid, EntryNew>) -> bool {
|
||||
self.attrs == rhs.attrs
|
||||
}
|
||||
|
||||
pub fn to_tombstone(&self) -> Self {
|
||||
// Duplicate this to a tombstone entry.
|
||||
let uuid_ava = self
|
||||
.get_ava(&String::from("uuid"))
|
||||
.expect("Corrupted entry!");
|
||||
let class_ava = vec!["object".to_string(), "tombstone".to_string()];
|
||||
|
||||
let mut attrs_new: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||
|
||||
attrs_new.insert("uuid".to_string(), uuid_ava.clone());
|
||||
attrs_new.insert("uuid".to_string(), vec![self.valid.uuid.clone()]);
|
||||
attrs_new.insert("class".to_string(), class_ava);
|
||||
|
||||
Entry {
|
||||
valid: EntryValid,
|
||||
valid: self.valid.clone(),
|
||||
state: self.state,
|
||||
attrs: attrs_new,
|
||||
}
|
||||
|
@ -445,12 +495,22 @@ impl Entry<EntryValid, EntryCommitted> {
|
|||
}
|
||||
|
||||
pub fn from_dbentry(db_e: DbEntry, id: u64) -> Self {
|
||||
let attrs = match db_e.ent {
|
||||
DbEntryVers::V1(v1) => v1.attrs,
|
||||
};
|
||||
|
||||
// TODO: Tidy this!
|
||||
let uuid: String = match attrs.get("uuid") {
|
||||
Some(vs) => vs.first(),
|
||||
None => None,
|
||||
}
|
||||
.expect("NO UUID PRESENT CORRUPT")
|
||||
.clone();
|
||||
|
||||
Entry {
|
||||
valid: EntryValid,
|
||||
valid: EntryValid { uuid: uuid },
|
||||
state: EntryCommitted { id },
|
||||
attrs: match db_e.ent {
|
||||
DbEntryVers::V1(v1) => v1.attrs,
|
||||
},
|
||||
attrs: attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -481,25 +541,8 @@ impl<STATE> Entry<EntryValid, STATE> {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn seal(self) -> Entry<EntryValid, EntryCommitted> {
|
||||
Entry {
|
||||
valid: self.valid,
|
||||
state: EntryCommitted {
|
||||
id: unimplemented!(),
|
||||
},
|
||||
attrs: self.attrs,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn get_uuid(&self) -> &String {
|
||||
// TODO: Make this not unwrap!!!
|
||||
self.attrs
|
||||
.get("uuid")
|
||||
.expect("UUID ATTR NOT PRESENT, INVALID ENTRY STATE!!!")
|
||||
.first()
|
||||
.expect("UUID VALUE NOT PRESENT, INVALID ENTRY STATE!!!")
|
||||
&self.valid.uuid
|
||||
}
|
||||
|
||||
// Assert if this filter matches the entry (no index)
|
||||
|
|
204
src/lib/event.rs
204
src/lib/event.rs
|
@ -63,9 +63,70 @@ impl SearchResult {
|
|||
// At the top we get "event types" and they contain the needed
|
||||
// actions, and a generic event component.
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EventOrigin {
|
||||
// External event, needs a UUID associated! Perhaps even an Entry/User to improve ACP checks?
|
||||
User(String),
|
||||
// Probably will bypass access profiles in many cases ...
|
||||
Internal,
|
||||
// Not used yet, but indicates that this change or event was triggered by a replication
|
||||
// event - may not even be needed ...
|
||||
// Replication,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
// The event's initiator aka origin source.
|
||||
// This importantly, is used for access control!
|
||||
pub origin: EventOrigin,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn from_request(
|
||||
_audit: &mut AuditScope,
|
||||
// _qs: &QueryServerTransaction,
|
||||
user_uuid: &str,
|
||||
) -> Result<Self, OperationError> {
|
||||
// Do we need to check or load the entry from the user_uuid?
|
||||
// In the future, probably yes.
|
||||
//
|
||||
// For now, no.
|
||||
Ok(Event {
|
||||
origin: EventOrigin::User(user_uuid.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_internal() -> Self {
|
||||
Event {
|
||||
origin: EventOrigin::Internal,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn from_impersonate_uuid(uuid: &str) -> Self {
|
||||
Event {
|
||||
origin: EventOrigin::User(uuid.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_impersonate(event: &Self) -> Self {
|
||||
// TODO: In the future, we could change some of this data
|
||||
// to reflect the fact we are infact impersonating the action
|
||||
// rather than the user explicitly requesting it. Could matter
|
||||
// to audits and logs to determine what happened.
|
||||
event.clone()
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn is_internal(&self) -> bool {
|
||||
match
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SearchEvent {
|
||||
pub internal: bool,
|
||||
pub event: Event,
|
||||
pub filter: Filter<FilterInvalid>,
|
||||
// TODO: Add list of attributes to request
|
||||
}
|
||||
|
@ -78,7 +139,11 @@ impl SearchEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
match Filter::from_ro(audit, &request.filter, qs) {
|
||||
Ok(f) => Ok(SearchEvent {
|
||||
internal: false,
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
//qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
filter: Filter::new_ignore_hidden(f),
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
|
@ -86,9 +151,17 @@ impl SearchEvent {
|
|||
}
|
||||
|
||||
// Just impersonate the account with no filter changes.
|
||||
pub fn new_impersonate(filter: Filter<FilterInvalid>) -> Self {
|
||||
#[cfg(test)]
|
||||
pub fn new_impersonate_uuid(user_uuid: &str, filter: Filter<FilterInvalid>) -> Self {
|
||||
SearchEvent {
|
||||
internal: false,
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
filter: filter,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_impersonate(event: &Event, filter: Filter<FilterInvalid>) -> Self {
|
||||
SearchEvent {
|
||||
event: Event::from_impersonate(event),
|
||||
filter: filter,
|
||||
}
|
||||
}
|
||||
|
@ -101,33 +174,38 @@ impl SearchEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
match Filter::from_ro(audit, &request.filter, qs) {
|
||||
Ok(f) => Ok(SearchEvent {
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
// qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
filter: Filter::new_recycled(f),
|
||||
internal: false,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new_rec_impersonate(filter: Filter<FilterInvalid>) -> Self {
|
||||
/* Impersonate a request for recycled objects */
|
||||
pub fn new_rec_impersonate_uuid(user_uuid: &str, filter: Filter<FilterInvalid>) -> Self {
|
||||
SearchEvent {
|
||||
internal: false,
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
filter: Filter::new_recycled(filter),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/* Impersonate an external request */
|
||||
pub fn new_ext_impersonate(filter: Filter<FilterInvalid>) -> Self {
|
||||
/* Impersonate an external request AKA filter ts + recycle */
|
||||
pub fn new_ext_impersonate_uuid(user_uuid: &str, filter: Filter<FilterInvalid>) -> Self {
|
||||
SearchEvent {
|
||||
internal: false,
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
filter: Filter::new_ignore_hidden(filter),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(filter: Filter<FilterInvalid>) -> Self {
|
||||
SearchEvent {
|
||||
internal: true,
|
||||
event: Event::from_internal(),
|
||||
filter: filter,
|
||||
}
|
||||
}
|
||||
|
@ -138,12 +216,12 @@ impl SearchEvent {
|
|||
// request is internal or not.
|
||||
#[derive(Debug)]
|
||||
pub struct CreateEvent {
|
||||
pub event: Event,
|
||||
// This may still actually change to handle the *raw* nature of the
|
||||
// input that we plan to parse.
|
||||
pub entries: Vec<Entry<EntryInvalid, EntryNew>>,
|
||||
/// Is the CreateEvent from an internal or external source?
|
||||
/// This may affect which plugins are run ...
|
||||
pub internal: bool,
|
||||
// Is the CreateEvent from an internal or external source?
|
||||
// This may affect which plugins are run ...
|
||||
}
|
||||
|
||||
// FIXME: Should this actually be in createEvent handler?
|
||||
|
@ -163,7 +241,11 @@ impl CreateEvent {
|
|||
// From ProtoEntry -> Entry
|
||||
// What is the correct consuming iterator here? Can we
|
||||
// even do that?
|
||||
internal: false,
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
// qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
entries: entries,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
|
@ -172,16 +254,19 @@ impl CreateEvent {
|
|||
|
||||
// Is this an internal only function?
|
||||
#[cfg(test)]
|
||||
pub fn from_vec(entries: Vec<Entry<EntryInvalid, EntryNew>>) -> Self {
|
||||
pub fn new_impersonate_uuid(
|
||||
user_uuid: &str,
|
||||
entries: Vec<Entry<EntryInvalid, EntryNew>>,
|
||||
) -> Self {
|
||||
CreateEvent {
|
||||
internal: false,
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(entries: Vec<Entry<EntryInvalid, EntryNew>>) -> Self {
|
||||
CreateEvent {
|
||||
internal: true,
|
||||
event: Event::from_internal(),
|
||||
entries: entries,
|
||||
}
|
||||
}
|
||||
|
@ -189,23 +274,23 @@ impl CreateEvent {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct ExistsEvent {
|
||||
pub event: Event,
|
||||
pub filter: Filter<FilterInvalid>,
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
impl ExistsEvent {
|
||||
pub fn new_internal(filter: Filter<FilterInvalid>) -> Self {
|
||||
ExistsEvent {
|
||||
event: Event::from_internal(),
|
||||
filter: filter,
|
||||
internal: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeleteEvent {
|
||||
pub event: Event,
|
||||
pub filter: Filter<FilterInvalid>,
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
impl DeleteEvent {
|
||||
|
@ -216,34 +301,38 @@ impl DeleteEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
match Filter::from_rw(audit, &request.filter, qs) {
|
||||
Ok(f) => Ok(DeleteEvent {
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
// qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
filter: Filter::new_ignore_hidden(f),
|
||||
internal: false,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn from_filter(filter: Filter<FilterInvalid>) -> Self {
|
||||
pub fn new_impersonate_uuid(user_uuid: &str, filter: Filter<FilterInvalid>) -> Self {
|
||||
DeleteEvent {
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
filter: filter,
|
||||
internal: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(filter: Filter<FilterInvalid>) -> Self {
|
||||
DeleteEvent {
|
||||
event: Event::from_internal(),
|
||||
filter: filter,
|
||||
internal: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ModifyEvent {
|
||||
pub event: Event,
|
||||
pub filter: Filter<FilterInvalid>,
|
||||
pub modlist: ModifyList<ModifyInvalid>,
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
impl ModifyEvent {
|
||||
|
@ -255,9 +344,13 @@ impl ModifyEvent {
|
|||
match Filter::from_rw(audit, &request.filter, qs) {
|
||||
Ok(f) => match ModifyList::from(audit, &request.modlist, qs) {
|
||||
Ok(m) => Ok(ModifyEvent {
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
// qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
filter: Filter::new_ignore_hidden(f),
|
||||
modlist: m,
|
||||
internal: false,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|
@ -266,37 +359,44 @@ impl ModifyEvent {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn from_filter(filter: Filter<FilterInvalid>, modlist: ModifyList<ModifyInvalid>) -> Self {
|
||||
ModifyEvent {
|
||||
filter: filter,
|
||||
modlist: modlist,
|
||||
internal: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(filter: Filter<FilterInvalid>, modlist: ModifyList<ModifyInvalid>) -> Self {
|
||||
ModifyEvent {
|
||||
event: Event::from_internal(),
|
||||
filter: filter,
|
||||
modlist: modlist,
|
||||
internal: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_impersonate(
|
||||
#[cfg(test)]
|
||||
pub fn new_impersonate_uuid(
|
||||
user_uuid: &str,
|
||||
filter: Filter<FilterInvalid>,
|
||||
modlist: ModifyList<ModifyInvalid>,
|
||||
) -> Self {
|
||||
ModifyEvent {
|
||||
event: Event::from_impersonate_uuid(user_uuid),
|
||||
filter: filter,
|
||||
modlist: modlist,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_impersonate(
|
||||
event: &Event,
|
||||
filter: Filter<FilterInvalid>,
|
||||
modlist: ModifyList<ModifyInvalid>,
|
||||
) -> Self {
|
||||
ModifyEvent {
|
||||
event: Event::from_impersonate(event),
|
||||
filter: filter,
|
||||
modlist: modlist,
|
||||
internal: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AuthEvent {}
|
||||
pub struct AuthEvent {
|
||||
// pub event: Event,
|
||||
}
|
||||
|
||||
impl AuthEvent {
|
||||
pub fn from_request(_request: AuthRequest) -> Self {
|
||||
|
@ -317,7 +417,9 @@ impl AuthResult {
|
|||
// TODO: Are these part of the proto?
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PurgeTombstoneEvent {}
|
||||
pub struct PurgeTombstoneEvent {
|
||||
pub event: Event,
|
||||
}
|
||||
|
||||
impl Message for PurgeTombstoneEvent {
|
||||
type Result = ();
|
||||
|
@ -325,12 +427,16 @@ impl Message for PurgeTombstoneEvent {
|
|||
|
||||
impl PurgeTombstoneEvent {
|
||||
pub fn new() -> Self {
|
||||
PurgeTombstoneEvent {}
|
||||
PurgeTombstoneEvent {
|
||||
event: Event::from_internal(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PurgeRecycledEvent {}
|
||||
pub struct PurgeRecycledEvent {
|
||||
pub event: Event,
|
||||
}
|
||||
|
||||
impl Message for PurgeRecycledEvent {
|
||||
type Result = ();
|
||||
|
@ -338,14 +444,16 @@ impl Message for PurgeRecycledEvent {
|
|||
|
||||
impl PurgeRecycledEvent {
|
||||
pub fn new() -> Self {
|
||||
PurgeRecycledEvent {}
|
||||
PurgeRecycledEvent {
|
||||
event: Event::from_internal(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReviveRecycledEvent {
|
||||
pub event: Event,
|
||||
pub filter: Filter<FilterInvalid>,
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
impl Message for ReviveRecycledEvent {
|
||||
|
@ -360,8 +468,12 @@ impl ReviveRecycledEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
match Filter::from_rw(audit, &request.filter, qs) {
|
||||
Ok(f) => Ok(ReviveRecycledEvent {
|
||||
event: Event::from_request(
|
||||
audit,
|
||||
// qs,
|
||||
request.user_uuid.as_str(),
|
||||
)?,
|
||||
filter: Filter::new_recycled(f),
|
||||
internal: false,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
|
|
@ -419,10 +419,13 @@ mod tests {
|
|||
fn test_or_entry_filter() {
|
||||
let e: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "db237e8a-0079-4b8c-8a56-593b22aa44d1"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"userid": ["william"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"uidNumber": ["1000"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -458,10 +461,13 @@ mod tests {
|
|||
fn test_and_entry_filter() {
|
||||
let e: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "db237e8a-0079-4b8c-8a56-593b22aa44d1"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"userid": ["william"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"uidNumber": ["1000"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -497,10 +503,13 @@ mod tests {
|
|||
fn test_not_entry_filter() {
|
||||
let e1: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "db237e8a-0079-4b8c-8a56-593b22aa44d1"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"userid": ["william"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"uidNumber": ["1000"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -524,10 +533,13 @@ mod tests {
|
|||
fn test_nested_entry_filter() {
|
||||
let e1: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "db237e8a-0079-4b8c-8a56-593b22aa44d1"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"uidNumber": ["1000"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -536,10 +548,13 @@ mod tests {
|
|||
|
||||
let e2: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "4b6228ab-1dbe-42a4-a9f5-f6368222438e"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"uuid": ["4b6228ab-1dbe-42a4-a9f5-f6368222438e"],
|
||||
"uidNumber": ["1001"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -548,10 +563,13 @@ mod tests {
|
|||
|
||||
let e3: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "7b23c99d-c06b-4a9a-a958-3afa56383e1d"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"uuid": ["7b23c99d-c06b-4a9a-a958-3afa56383e1d"],
|
||||
"uidNumber": ["1002"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -560,10 +578,13 @@ mod tests {
|
|||
|
||||
let e4: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "21d816b5-1f6a-4696-b7c1-6ed06d22ed81"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["group"],
|
||||
"uuid": ["21d816b5-1f6a-4696-b7c1-6ed06d22ed81"],
|
||||
"uidNumber": ["1000"]
|
||||
}
|
||||
}"#,
|
||||
|
|
|
@ -34,7 +34,8 @@ mod log;
|
|||
#[macro_use]
|
||||
mod audit;
|
||||
mod be;
|
||||
mod constants;
|
||||
// TODO: Should this be public?
|
||||
pub mod constants;
|
||||
mod entry;
|
||||
mod event;
|
||||
mod identity;
|
||||
|
|
|
@ -48,7 +48,6 @@ impl Plugin for Base {
|
|||
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
|
||||
_ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
let name_uuid = String::from("uuid");
|
||||
// For each candidate
|
||||
for entry in cand.iter_mut() {
|
||||
audit_log!(au, "Base check on entry: {:?}", entry);
|
||||
|
@ -62,7 +61,7 @@ impl Plugin for Base {
|
|||
|
||||
// if they don't have uuid, create it.
|
||||
// TODO: get_ava should have a str version for effeciency?
|
||||
let c_uuid: String = match entry.get_ava(&name_uuid) {
|
||||
let c_uuid: String = match entry.get_ava(&"uuid".to_string()) {
|
||||
Some(u) => {
|
||||
// Actually check we have a value, could be empty array ...
|
||||
// TODO: Should this be left to schema to assert the value?
|
||||
|
@ -87,7 +86,7 @@ impl Plugin for Base {
|
|||
audit_log!(au, "Setting temporary UUID {} to entry", c_uuid);
|
||||
let ava_uuid: Vec<String> = vec![c_uuid];
|
||||
|
||||
entry.set_avas(name_uuid.clone(), ava_uuid);
|
||||
entry.set_avas("uuid".to_string(), ava_uuid);
|
||||
audit_log!(au, "Temporary entry state: {:?}", entry);
|
||||
}
|
||||
|
||||
|
@ -98,7 +97,7 @@ impl Plugin for Base {
|
|||
// that a duplicate exists.
|
||||
for entry in cand.iter() {
|
||||
let uuid_ref = entry
|
||||
.get_ava(&name_uuid)
|
||||
.get_ava(&"uuid".to_string())
|
||||
.ok_or(OperationError::Plugin)?
|
||||
.first()
|
||||
.ok_or(OperationError::Plugin)?;
|
||||
|
@ -173,7 +172,6 @@ impl Plugin for Base {
|
|||
au: &mut AuditScope,
|
||||
qs: &QueryServerTransaction,
|
||||
) -> Vec<Result<(), ConsistencyError>> {
|
||||
let name_uuid = String::from("uuid");
|
||||
// Verify all uuid's are unique?
|
||||
// Probably the literally worst thing ...
|
||||
|
||||
|
@ -190,35 +188,24 @@ impl Plugin for Base {
|
|||
.iter()
|
||||
// do an exists checks on the uuid
|
||||
.map(|e| {
|
||||
// TODO: Could this be better?
|
||||
let uuid = match e.get_ava(&name_uuid) {
|
||||
Some(u) => {
|
||||
if u.len() == 1 {
|
||||
Ok(u.first().expect("Ohh ffs, really?").clone())
|
||||
} else {
|
||||
Err(ConsistencyError::EntryUuidCorrupt(e.get_id()))
|
||||
}
|
||||
}
|
||||
None => Err(ConsistencyError::EntryUuidCorrupt(e.get_id())),
|
||||
};
|
||||
// To get the entry deserialised, a UUID MUST EXIST, else an expect
|
||||
// will be thrown in the deserialise (possibly it will be better
|
||||
// handled later). But it means this check only needs to validate
|
||||
// uniqueness!
|
||||
let uuid: &String = e.get_uuid();
|
||||
|
||||
match uuid {
|
||||
Ok(u) => {
|
||||
let filt = Filter::Eq(name_uuid.clone(), u.clone());
|
||||
match qs.internal_search(au, filt) {
|
||||
Ok(r) => {
|
||||
if r.len() == 0 {
|
||||
Err(ConsistencyError::UuidIndexCorrupt(u))
|
||||
} else if r.len() == 1 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ConsistencyError::UuidNotUnique(u))
|
||||
}
|
||||
}
|
||||
Err(_) => Err(ConsistencyError::QueryServerSearchFailure),
|
||||
let filt = Filter::Eq("uuid".to_string(), uuid.to_string());
|
||||
match qs.internal_search(au, filt) {
|
||||
Ok(r) => {
|
||||
if r.len() == 0 {
|
||||
Err(ConsistencyError::UuidIndexCorrupt(uuid.to_string()))
|
||||
} else if r.len() == 1 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ConsistencyError::UuidNotUnique(uuid.to_string()))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
Err(_) => Err(ConsistencyError::QueryServerSearchFailure),
|
||||
}
|
||||
})
|
||||
.filter(|v| v.is_err())
|
||||
|
@ -277,7 +264,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
let cands = qs
|
||||
.internal_search(au, Filter::Eq("name".to_string(), "testperson".to_string()))
|
||||
|
@ -314,7 +301,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -345,7 +332,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -376,7 +363,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
let cands = qs
|
||||
.internal_search(au, Filter::Eq("name".to_string(), "testperson".to_string()))
|
||||
|
@ -412,7 +399,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -449,7 +436,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -495,7 +482,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -528,7 +515,7 @@ mod tests {
|
|||
"uuid".to_string(),
|
||||
"f15a7219-1d15-44e3-a7b4-bec899c07788".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -560,7 +547,7 @@ mod tests {
|
|||
"uuid".to_string(),
|
||||
"f15a7219-1d15-44e3-a7b4-bec899c07788".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -589,7 +576,7 @@ mod tests {
|
|||
preload,
|
||||
Filter::Eq("name".to_string(), "testgroup_a".to_string()),
|
||||
ModifyList::new_list(vec![Modify::Purged("uuid".to_string())]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ macro_rules! run_create_test {
|
|||
$expect:expr,
|
||||
$preload_entries:ident,
|
||||
$create_entries:ident,
|
||||
$internal:ident,
|
||||
$internal:expr,
|
||||
$check:expr
|
||||
) => {{
|
||||
use crate::audit::AuditScope;
|
||||
|
@ -48,10 +48,9 @@ macro_rules! run_create_test {
|
|||
audit_segment!(au, || {
|
||||
let qs = setup_test!(&mut au, $preload_entries);
|
||||
|
||||
let ce = if $internal {
|
||||
CreateEvent::new_internal($create_entries.clone())
|
||||
} else {
|
||||
CreateEvent::from_vec($create_entries.clone())
|
||||
let ce = match $internal {
|
||||
None => CreateEvent::new_internal($create_entries.clone()),
|
||||
Some(uuid) => CreateEvent::new_impersonate_uuid(uuid, $create_entries.clone()),
|
||||
};
|
||||
|
||||
let mut au_test = AuditScope::new("create_test");
|
||||
|
@ -86,7 +85,7 @@ macro_rules! run_modify_test {
|
|||
$preload_entries:ident,
|
||||
$modify_filter:expr,
|
||||
$modify_list:expr,
|
||||
$internal:ident,
|
||||
$internal:expr,
|
||||
$check:expr
|
||||
) => {{
|
||||
use crate::audit::AuditScope;
|
||||
|
@ -100,10 +99,9 @@ macro_rules! run_modify_test {
|
|||
audit_segment!(au, || {
|
||||
let qs = setup_test!(&mut au, $preload_entries);
|
||||
|
||||
let me = if $internal {
|
||||
ModifyEvent::new_internal($modify_filter, $modify_list)
|
||||
} else {
|
||||
ModifyEvent::from_filter($modify_filter, $modify_list)
|
||||
let me = match $internal {
|
||||
None => ModifyEvent::new_internal($modify_filter, $modify_list),
|
||||
Some(uuid) => ModifyEvent::new_impersonate_uuid(uuid, $modify_filter, $modify_list),
|
||||
};
|
||||
|
||||
let mut au_test = AuditScope::new("modify_test");
|
||||
|
@ -137,7 +135,7 @@ macro_rules! run_delete_test {
|
|||
$expect:expr,
|
||||
$preload_entries:ident,
|
||||
$delete_filter:expr,
|
||||
$internal:ident,
|
||||
$internal:expr,
|
||||
$check:expr
|
||||
) => {{
|
||||
use crate::audit::AuditScope;
|
||||
|
@ -151,10 +149,9 @@ macro_rules! run_delete_test {
|
|||
audit_segment!(au, || {
|
||||
let qs = setup_test!(&mut au, $preload_entries);
|
||||
|
||||
let de = if $internal {
|
||||
DeleteEvent::new_internal($delete_filter.clone())
|
||||
} else {
|
||||
DeleteEvent::from_filter($delete_filter.clone())
|
||||
let de = match $internal {
|
||||
Some(uuid) => DeleteEvent::new_impersonate_uuid(uuid, $delete_filter.clone()),
|
||||
None => DeleteEvent::new_internal($delete_filter.clone()),
|
||||
};
|
||||
|
||||
let mut au_test = AuditScope::new("delete_test");
|
||||
|
|
|
@ -509,7 +509,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -543,7 +543,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -598,7 +598,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -660,7 +660,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -727,7 +727,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_B.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -765,7 +765,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_B.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -821,7 +821,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_C.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -880,7 +880,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_A.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -950,7 +950,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_A.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1020,7 +1020,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_B.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1061,7 +1061,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_B.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1121,7 +1121,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_C.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1191,7 +1191,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
UUID_A.to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1280,7 +1280,7 @@ mod tests {
|
|||
Modify::Removed("member".to_string(), UUID_A.to_string()),
|
||||
Modify::Removed("member".to_string(), UUID_D.to_string()),
|
||||
]),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1344,7 +1344,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("uuid".to_string(), UUID_A.to_string()),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1381,7 +1381,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("uuid".to_string(), UUID_A.to_string()),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1428,7 +1428,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("uuid".to_string(), UUID_B.to_string()),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1484,7 +1484,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("uuid".to_string(), UUID_A.to_string()),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
@ -1554,7 +1554,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("uuid".to_string(), UUID_B.to_string()),
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
// V-- this uuid is
|
||||
// V-- memberof this UUID
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
|
||||
// Don't allow setting class = recycle/tombstone during any
|
||||
// operation unless internal == true OR delete.
|
||||
|
|
|
@ -141,19 +141,12 @@ impl Plugin for ReferentialIntegrity {
|
|||
// Delete is pretty different to the other pre checks. This is
|
||||
// actually the bulk of the work we'll do to clean up references
|
||||
// when they are deleted.
|
||||
let uuid_name = "uuid".to_string();
|
||||
|
||||
// Find all reference types in the schema
|
||||
let schema = qs.get_schema();
|
||||
let ref_types = schema.get_reference_types();
|
||||
// Get the UUID of all entries we are deleting
|
||||
let uuids: Vec<&String> = cand
|
||||
.iter()
|
||||
.map(|e| e.get_ava(&uuid_name).ok_or(OperationError::Plugin))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
let uuids: Vec<&String> = cand.iter().map(|e| e.get_uuid()).collect();
|
||||
|
||||
// Generate a filter which is the set of all schema reference types
|
||||
// as EQ to all uuid of all entries in delete. - this INCLUDES recycled
|
||||
|
@ -197,7 +190,6 @@ impl Plugin for ReferentialIntegrity {
|
|||
au: &mut AuditScope,
|
||||
qs: &QueryServerTransaction,
|
||||
) -> Vec<Result<(), ConsistencyError>> {
|
||||
let name_uuid = "uuid".to_string();
|
||||
// Get all entries as cand
|
||||
// build a cand-uuid set
|
||||
let filt_in: Filter<FilterInvalid> =
|
||||
|
@ -211,30 +203,9 @@ impl Plugin for ReferentialIntegrity {
|
|||
Err(e) => return vec![e],
|
||||
};
|
||||
|
||||
let (acu, err): (
|
||||
Vec<Result<&String, ConsistencyError>>,
|
||||
Vec<Result<&String, ConsistencyError>>,
|
||||
) = all_cand
|
||||
.iter()
|
||||
.map(|e| {
|
||||
e.get_ava(&name_uuid)
|
||||
.ok_or(ConsistencyError::EntryUuidCorrupt(e.get_id()))
|
||||
.map(|v| v.first().expect("Can not fail!!!"))
|
||||
})
|
||||
.partition(|v| v.is_ok());
|
||||
let acu: Vec<&String> = all_cand.iter().map(|e| e.get_uuid()).collect();
|
||||
|
||||
if err.len() > 0 {
|
||||
return err
|
||||
.into_iter()
|
||||
.map(|v| Err(v.expect_err("Can not fail!!!")))
|
||||
.collect();
|
||||
}
|
||||
|
||||
let acu_map: HashMap<&String, ()> = acu
|
||||
.into_iter()
|
||||
.map(|v| v.expect("Can not fail!!!"))
|
||||
.map(|v| (v, ()))
|
||||
.collect();
|
||||
let acu_map: HashMap<&String, ()> = acu.into_iter().map(|v| (v, ())).collect();
|
||||
|
||||
let schema = qs.get_schema();
|
||||
let ref_types = schema.get_reference_types();
|
||||
|
@ -296,7 +267,7 @@ mod tests {
|
|||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -339,7 +310,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
let cands = qs
|
||||
.internal_search(
|
||||
|
@ -378,7 +349,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
None,
|
||||
|au: &mut AuditScope, qs: &QueryServerWriteTransaction| {
|
||||
let cands = qs
|
||||
.internal_search(au, Filter::Eq("name".to_string(), "testgroup".to_string()))
|
||||
|
@ -428,7 +399,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
"d2b496bd-8493-47b7-8142-f568b5cf47ee".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -459,7 +430,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
"d2b496bd-8493-47b7-8142-f568b5cf47ee".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -502,7 +473,7 @@ mod tests {
|
|||
preload,
|
||||
Filter::Eq("name".to_string(), "testgroup_b".to_string()),
|
||||
ModifyList::new_list(vec![Modify::Purged("member".to_string())]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -534,7 +505,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
"d2b496bd-8493-47b7-8142-f568b5cf47ee".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -579,7 +550,7 @@ mod tests {
|
|||
"member".to_string(),
|
||||
"d2b496bd-8493-47b7-8142-f568b5cf47ee".to_string()
|
||||
)]),
|
||||
false,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
@ -623,7 +594,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("name".to_string(), "testgroup_a".to_string()),
|
||||
false,
|
||||
None,
|
||||
|_au: &mut AuditScope, _qs: &QueryServerWriteTransaction| {}
|
||||
);
|
||||
}
|
||||
|
@ -671,7 +642,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("name".to_string(), "testgroup_b".to_string()),
|
||||
false,
|
||||
None,
|
||||
|_au: &mut AuditScope, _qs: &QueryServerWriteTransaction| {}
|
||||
);
|
||||
}
|
||||
|
@ -700,7 +671,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
Filter::Eq("name".to_string(), "testgroup_b".to_string()),
|
||||
false,
|
||||
None,
|
||||
|_au: &mut AuditScope, _qs: &QueryServerWriteTransaction| {}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -59,11 +59,15 @@ impl OperationResponse {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
pub filter: Filter,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl SearchRequest {
|
||||
pub fn new(filter: Filter) -> Self {
|
||||
SearchRequest { filter: filter }
|
||||
pub fn new(filter: Filter, user_uuid: &str) -> Self {
|
||||
SearchRequest {
|
||||
filter: filter,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,11 +89,15 @@ impl SearchResponse {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateRequest {
|
||||
pub entries: Vec<Entry>,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl CreateRequest {
|
||||
pub fn new(entries: Vec<Entry>) -> Self {
|
||||
CreateRequest { entries: entries }
|
||||
pub fn new(entries: Vec<Entry>, user_uuid: &str) -> Self {
|
||||
CreateRequest {
|
||||
entries: entries,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,11 +108,15 @@ impl Message for CreateRequest {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DeleteRequest {
|
||||
pub filter: Filter,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl DeleteRequest {
|
||||
pub fn new(filter: Filter) -> Self {
|
||||
DeleteRequest { filter: filter }
|
||||
pub fn new(filter: Filter, user_uuid: &str) -> Self {
|
||||
DeleteRequest {
|
||||
filter: filter,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,13 +129,15 @@ pub struct ModifyRequest {
|
|||
// Probably needs a modlist?
|
||||
pub filter: Filter,
|
||||
pub modlist: ModifyList,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl ModifyRequest {
|
||||
pub fn new(filter: Filter, modlist: ModifyList) -> Self {
|
||||
pub fn new(filter: Filter, modlist: ModifyList, user_uuid: &str) -> Self {
|
||||
ModifyRequest {
|
||||
filter: filter,
|
||||
modlist: modlist,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +173,7 @@ pub enum AuthState {
|
|||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AuthRequest {
|
||||
pub state: AuthState,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl Message for AuthRequest {
|
||||
|
@ -191,11 +206,15 @@ pub struct AuthResponse {
|
|||
|
||||
pub struct SearchRecycledRequest {
|
||||
pub filter: Filter,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl SearchRecycledRequest {
|
||||
pub fn new(filter: Filter) -> Self {
|
||||
SearchRecycledRequest { filter: filter }
|
||||
pub fn new(filter: Filter, user_uuid: &str) -> Self {
|
||||
SearchRecycledRequest {
|
||||
filter: filter,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,10 +222,14 @@ impl SearchRecycledRequest {
|
|||
|
||||
pub struct ReviveRecycledRequest {
|
||||
pub filter: Filter,
|
||||
pub user_uuid: String,
|
||||
}
|
||||
|
||||
impl ReviveRecycledRequest {
|
||||
pub fn new(filter: Filter) -> Self {
|
||||
ReviveRecycledRequest { filter: filter }
|
||||
pub fn new(filter: Filter, user_uuid: &str) -> Self {
|
||||
ReviveRecycledRequest {
|
||||
filter: filter,
|
||||
user_uuid: user_uuid.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1288,7 +1288,7 @@ mod tests {
|
|||
let mut audit = AuditScope::new("test_schema_entries");
|
||||
let schema_outer = Schema::new(&mut audit).expect("failed to create schema");
|
||||
let schema = schema_outer.read();
|
||||
let e_no_class: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
let e_no_uuid: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
|
@ -1297,6 +1297,22 @@ mod tests {
|
|||
)
|
||||
.expect("json parse failure");
|
||||
|
||||
assert_eq!(
|
||||
e_no_uuid.validate(&schema),
|
||||
Err(SchemaError::MissingMustAttribute("uuid".to_string()))
|
||||
);
|
||||
|
||||
let e_no_class: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"]
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.expect("json parse failure");
|
||||
|
||||
assert_eq!(e_no_class.validate(&schema), Err(SchemaError::InvalidClass));
|
||||
|
||||
let e_bad_class: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
|
@ -1304,6 +1320,7 @@ mod tests {
|
|||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"class": ["zzzzzz"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1319,6 +1336,7 @@ mod tests {
|
|||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"class": ["attributetype"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1343,6 +1361,7 @@ mod tests {
|
|||
"secret": ["false"],
|
||||
"multivalue": ["false"],
|
||||
"syntax": ["UTF8STRING"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"zzzzz": ["zzzz"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1365,6 +1384,7 @@ mod tests {
|
|||
"system": ["false"],
|
||||
"secret": ["false"],
|
||||
"multivalue": ["zzzzz"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1387,6 +1407,7 @@ mod tests {
|
|||
"system": ["false"],
|
||||
"secret": ["false"],
|
||||
"multivalue": ["true"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1419,6 +1440,7 @@ mod tests {
|
|||
"name": ["TestPerson"],
|
||||
"displayName": ["testperson"],
|
||||
"syntax": ["utf8string"],
|
||||
"UUID": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"index": ["equality"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1427,13 +1449,16 @@ mod tests {
|
|||
|
||||
let e_expect: Entry<EntryValid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"valid": {
|
||||
"uuid": "db237e8a-0079-4b8c-8a56-593b22aa44d1"
|
||||
},
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["extensibleobject"],
|
||||
"name": ["testperson"],
|
||||
"displayname": ["testperson"],
|
||||
"syntax": ["UTF8STRING"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"index": ["EQUALITY"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1459,6 +1484,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["extensibleobject"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"secret": ["zzzz"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1476,6 +1502,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["extensibleobject"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"secret": ["true"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1511,6 +1538,7 @@ mod tests {
|
|||
"name": ["testperson"],
|
||||
"principal_name": ["testperson@project.org"],
|
||||
"description": ["testperson"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"displayname": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1526,6 +1554,7 @@ mod tests {
|
|||
"class": ["group"],
|
||||
"name": ["testgroup"],
|
||||
"principal_name": ["testgroup@project.org"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"description": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
|
|
|
@ -10,7 +10,8 @@ use crate::constants::{JSON_ANONYMOUS_V1, JSON_SYSTEM_INFO_V1};
|
|||
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
|
||||
use crate::error::{ConsistencyError, OperationError, SchemaError};
|
||||
use crate::event::{
|
||||
CreateEvent, DeleteEvent, ExistsEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent,
|
||||
CreateEvent, DeleteEvent, Event, EventOrigin, ExistsEvent, ModifyEvent, ReviveRecycledEvent,
|
||||
SearchEvent,
|
||||
};
|
||||
use crate::filter::{Filter, FilterInvalid};
|
||||
use crate::modify::{Modify, ModifyInvalid, ModifyList};
|
||||
|
@ -164,13 +165,7 @@ pub trait QueryServerReadTransaction {
|
|||
// TODO: fine for 0/1 case, but check len for >= 2 to eliminate that case.
|
||||
let e = res.first().ok_or(OperationError::NoMatchingEntries)?;
|
||||
// Get the uuid from the entry. Again, check it exists, and only one.
|
||||
let uuid_res = match e.get_ava(&String::from("uuid")) {
|
||||
Some(vas) => match vas.first() {
|
||||
Some(u) => u.clone(),
|
||||
None => return Err(OperationError::InvalidEntryState),
|
||||
},
|
||||
None => return Err(OperationError::InvalidEntryState),
|
||||
};
|
||||
let uuid_res: String = e.get_uuid().to_string();
|
||||
|
||||
audit_log!(audit, "name_to_uuid: uuid <- {:?}", uuid_res);
|
||||
|
||||
|
@ -251,9 +246,10 @@ pub trait QueryServerReadTransaction {
|
|||
&self,
|
||||
audit: &mut AuditScope,
|
||||
filter: Filter<FilterInvalid>,
|
||||
event: &Event,
|
||||
) -> Result<Vec<Entry<EntryValid, EntryCommitted>>, OperationError> {
|
||||
let mut audit_int = AuditScope::new("impersonate_search");
|
||||
let se = SearchEvent::new_impersonate(filter);
|
||||
let se = SearchEvent::new_impersonate(event, filter);
|
||||
let res = self.search(&mut audit_int, &se);
|
||||
audit.append_scope(audit_int);
|
||||
res
|
||||
|
@ -583,7 +579,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// We only need to retrieve uuid though ...
|
||||
|
||||
// Now, delete only what you can see
|
||||
let pre_candidates = match self.impersonate_search(au, de.filter.clone()) {
|
||||
let pre_candidates = match self.impersonate_search(au, de.filter.clone(), &de.event) {
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
audit_log!(au, "delete: error in pre-candidate selection {:?}", e);
|
||||
|
@ -750,7 +746,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
)]);
|
||||
|
||||
// Now impersonate the modify
|
||||
self.impersonate_modify(au, re.filter.clone(), modlist)
|
||||
self.impersonate_modify(au, re.filter.clone(), modlist, &re.event)
|
||||
}
|
||||
|
||||
pub fn modify(&self, au: &mut AuditScope, me: &ModifyEvent) -> Result<(), OperationError> {
|
||||
|
@ -780,7 +776,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// TODO: Fix this filter clone ....
|
||||
// Likely this will be fixed if search takes &filter, and then clone
|
||||
// to normalise, instead of attempting to mut the filter on norm.
|
||||
let pre_candidates = match self.impersonate_search(au, me.filter.clone()) {
|
||||
let pre_candidates = match self.impersonate_search(au, me.filter.clone(), &me.event) {
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
audit_log!(au, "modify: error in pre-candidate selection {:?}", e);
|
||||
|
@ -789,20 +785,23 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
};
|
||||
|
||||
if pre_candidates.len() == 0 {
|
||||
if me.internal {
|
||||
audit_log!(
|
||||
au,
|
||||
"modify: no candidates match filter ... continuing {:?}",
|
||||
me.filter
|
||||
);
|
||||
return Ok(());
|
||||
} else {
|
||||
audit_log!(
|
||||
au,
|
||||
"modify: no candidates match filter, failure {:?}",
|
||||
me.filter
|
||||
);
|
||||
return Err(OperationError::NoMatchingEntries);
|
||||
match me.event.origin {
|
||||
EventOrigin::Internal => {
|
||||
audit_log!(
|
||||
au,
|
||||
"modify: no candidates match filter ... continuing {:?}",
|
||||
me.filter
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
audit_log!(
|
||||
au,
|
||||
"modify: no candidates match filter, failure {:?}",
|
||||
me.filter
|
||||
);
|
||||
return Err(OperationError::NoMatchingEntries);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -937,9 +936,10 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
audit: &mut AuditScope,
|
||||
filter: Filter<FilterInvalid>,
|
||||
modlist: ModifyList<ModifyInvalid>,
|
||||
event: &Event,
|
||||
) -> Result<(), OperationError> {
|
||||
let mut audit_int = AuditScope::new("impersonate_modify");
|
||||
let me = ModifyEvent::new_impersonate(filter, modlist);
|
||||
let me = ModifyEvent::new_impersonate(event, filter, modlist);
|
||||
let res = self.modify(&mut audit_int, &me);
|
||||
audit.append_scope(audit_int);
|
||||
res
|
||||
|
@ -1134,6 +1134,7 @@ mod tests {
|
|||
|
||||
use crate::audit::AuditScope;
|
||||
use crate::be::Backend;
|
||||
use crate::constants::UUID_ADMIN;
|
||||
use crate::entry::{Entry, EntryInvalid, EntryNew};
|
||||
use crate::error::{OperationError, SchemaError};
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent};
|
||||
|
@ -1174,8 +1175,8 @@ mod tests {
|
|||
let server_txn = server.write();
|
||||
let filt = Filter::Pres(String::from("name"));
|
||||
|
||||
let se1 = SearchEvent::new_impersonate(filt.clone());
|
||||
let se2 = SearchEvent::new_impersonate(filt);
|
||||
let se1 = SearchEvent::new_impersonate_uuid(UUID_ADMIN, filt.clone());
|
||||
let se2 = SearchEvent::new_impersonate_uuid(UUID_ADMIN, filt);
|
||||
|
||||
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
r#"{
|
||||
|
@ -1192,7 +1193,7 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e.clone()]);
|
||||
let ce = CreateEvent::new_internal(vec![e.clone()]);
|
||||
|
||||
let r1 = server_txn.search(audit, &se1).expect("search failure");
|
||||
assert!(r1.len() == 0);
|
||||
|
@ -1276,20 +1277,21 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e1.clone(), e2.clone()]);
|
||||
let ce = CreateEvent::new_internal(vec![e1.clone(), e2.clone()]);
|
||||
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
// Empty Modlist (filter is valid)
|
||||
let me_emp = ModifyEvent::from_filter(
|
||||
let me_emp = ModifyEvent::new_internal(
|
||||
Filter::Pres(String::from("class")),
|
||||
ModifyList::new_list(vec![]),
|
||||
);
|
||||
assert!(server_txn.modify(audit, &me_emp) == Err(OperationError::EmptyRequest));
|
||||
|
||||
// Mod changes no objects
|
||||
let me_nochg = ModifyEvent::from_filter(
|
||||
let me_nochg = ModifyEvent::new_impersonate_uuid(
|
||||
UUID_ADMIN,
|
||||
Filter::Eq(String::from("name"), String::from("flarbalgarble")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("description"),
|
||||
|
@ -1299,7 +1301,7 @@ mod tests {
|
|||
assert!(server_txn.modify(audit, &me_nochg) == Err(OperationError::NoMatchingEntries));
|
||||
|
||||
// Filter is invalid to schema
|
||||
let me_inv_f = ModifyEvent::from_filter(
|
||||
let me_inv_f = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("tnanuanou"), String::from("Flarbalgarble")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("description"),
|
||||
|
@ -1314,7 +1316,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Mod is invalid to schema
|
||||
let me_inv_m = ModifyEvent::from_filter(
|
||||
let me_inv_m = ModifyEvent::new_internal(
|
||||
Filter::Pres(String::from("class")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("htnaonu"),
|
||||
|
@ -1329,7 +1331,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Mod single object
|
||||
let me_sin = ModifyEvent::from_filter(
|
||||
let me_sin = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("name"), String::from("testperson2")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("description"),
|
||||
|
@ -1339,7 +1341,7 @@ mod tests {
|
|||
assert!(server_txn.modify(audit, &me_sin).is_ok());
|
||||
|
||||
// Mod multiple object
|
||||
let me_mult = ModifyEvent::from_filter(
|
||||
let me_mult = ModifyEvent::new_internal(
|
||||
Filter::Or(vec![
|
||||
Filter::Eq(String::from("name"), String::from("testperson1")),
|
||||
Filter::Eq(String::from("name"), String::from("testperson2")),
|
||||
|
@ -1377,13 +1379,13 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e1.clone()]);
|
||||
let ce = CreateEvent::new_internal(vec![e1.clone()]);
|
||||
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
// Add class but no values
|
||||
let me_sin = ModifyEvent::from_filter(
|
||||
let me_sin = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("name"), String::from("testperson1")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("class"),
|
||||
|
@ -1393,7 +1395,7 @@ mod tests {
|
|||
assert!(server_txn.modify(audit, &me_sin).is_err());
|
||||
|
||||
// Add multivalue where not valid
|
||||
let me_sin = ModifyEvent::from_filter(
|
||||
let me_sin = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("name"), String::from("testperson1")),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
String::from("name"),
|
||||
|
@ -1403,7 +1405,7 @@ mod tests {
|
|||
assert!(server_txn.modify(audit, &me_sin).is_err());
|
||||
|
||||
// add class and valid values?
|
||||
let me_sin = ModifyEvent::from_filter(
|
||||
let me_sin = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("name"), String::from("testperson1")),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Present(String::from("class"), String::from("system_info")),
|
||||
|
@ -1414,7 +1416,7 @@ mod tests {
|
|||
assert!(server_txn.modify(audit, &me_sin).is_ok());
|
||||
|
||||
// Replace a value
|
||||
let me_sin = ModifyEvent::from_filter(
|
||||
let me_sin = ModifyEvent::new_internal(
|
||||
Filter::Eq(String::from("name"), String::from("testperson1")),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Purged("name".to_string()),
|
||||
|
@ -1476,31 +1478,31 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e1.clone(), e2.clone(), e3.clone()]);
|
||||
let ce = CreateEvent::new_internal(vec![e1.clone(), e2.clone(), e3.clone()]);
|
||||
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
// Delete filter is syntax invalid
|
||||
let de_inv = DeleteEvent::from_filter(Filter::Pres(String::from("nhtoaunaoehtnu")));
|
||||
let de_inv = DeleteEvent::new_internal(Filter::Pres(String::from("nhtoaunaoehtnu")));
|
||||
assert!(server_txn.delete(audit, &de_inv).is_err());
|
||||
|
||||
// Delete deletes nothing
|
||||
let de_empty = DeleteEvent::from_filter(Filter::Eq(
|
||||
let de_empty = DeleteEvent::new_internal(Filter::Eq(
|
||||
String::from("uuid"),
|
||||
String::from("cc8e95b4-c24f-4d68-ba54-000000000000"),
|
||||
));
|
||||
assert!(server_txn.delete(audit, &de_empty).is_err());
|
||||
|
||||
// Delete matches one
|
||||
let de_sin = DeleteEvent::from_filter(Filter::Eq(
|
||||
let de_sin = DeleteEvent::new_internal(Filter::Eq(
|
||||
String::from("name"),
|
||||
String::from("testperson3"),
|
||||
));
|
||||
assert!(server_txn.delete(audit, &de_sin).is_ok());
|
||||
|
||||
// Delete matches many
|
||||
let de_mult = DeleteEvent::from_filter(Filter::Eq(
|
||||
let de_mult = DeleteEvent::new_internal(Filter::Eq(
|
||||
String::from("description"),
|
||||
String::from("testperson"),
|
||||
));
|
||||
|
@ -1529,14 +1531,18 @@ mod tests {
|
|||
String::from("class"),
|
||||
String::from("tombstone"),
|
||||
)]),
|
||||
UUID_ADMIN,
|
||||
),
|
||||
&server_txn,
|
||||
)
|
||||
.expect("modify event create failed");
|
||||
let de_ts =
|
||||
DeleteEvent::from_request(audit, DeleteRequest::new(filt_ts.clone()), &server_txn)
|
||||
.expect("delete event create failed");
|
||||
let se_ts = SearchEvent::new_ext_impersonate(filt_i_ts.clone());
|
||||
let de_ts = DeleteEvent::from_request(
|
||||
audit,
|
||||
DeleteRequest::new(filt_ts.clone(), UUID_ADMIN),
|
||||
&server_txn,
|
||||
)
|
||||
.expect("delete event create failed");
|
||||
let se_ts = SearchEvent::new_ext_impersonate_uuid(UUID_ADMIN, filt_i_ts.clone());
|
||||
|
||||
// First, create a tombstone
|
||||
let e_ts: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
|
||||
|
@ -1551,7 +1557,7 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e_ts]);
|
||||
let ce = CreateEvent::new_internal(vec![e_ts]);
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
|
@ -1610,23 +1616,27 @@ mod tests {
|
|||
String::from("class"),
|
||||
String::from("recycled"),
|
||||
)]),
|
||||
UUID_ADMIN,
|
||||
),
|
||||
&server_txn,
|
||||
)
|
||||
.expect("modify event create failed");
|
||||
let de_rc =
|
||||
DeleteEvent::from_request(audit, DeleteRequest::new(filt_rc.clone()), &server_txn)
|
||||
.expect("delete event create failed");
|
||||
let se_rc = SearchEvent::new_ext_impersonate(filt_i_rc.clone());
|
||||
let de_rc = DeleteEvent::from_request(
|
||||
audit,
|
||||
DeleteRequest::new(filt_rc.clone(), UUID_ADMIN),
|
||||
&server_txn,
|
||||
)
|
||||
.expect("delete event create failed");
|
||||
let se_rc = SearchEvent::new_ext_impersonate_uuid(UUID_ADMIN, filt_i_rc.clone());
|
||||
|
||||
let sre_rc = SearchEvent::new_rec_impersonate(filt_i_rc.clone());
|
||||
let sre_rc = SearchEvent::new_rec_impersonate_uuid(UUID_ADMIN, filt_i_rc.clone());
|
||||
|
||||
let rre_rc = ReviveRecycledEvent::from_request(
|
||||
audit,
|
||||
ReviveRecycledRequest::new(ProtoFilter::Eq(
|
||||
"name".to_string(),
|
||||
"testperson1".to_string(),
|
||||
)),
|
||||
ReviveRecycledRequest::new(
|
||||
ProtoFilter::Eq("name".to_string(), "testperson1".to_string()),
|
||||
UUID_ADMIN,
|
||||
),
|
||||
&server_txn,
|
||||
)
|
||||
.expect("revive recycled create failed");
|
||||
|
@ -1662,7 +1672,7 @@ mod tests {
|
|||
)
|
||||
.expect("json failure");
|
||||
|
||||
let ce = CreateEvent::from_vec(vec![e1, e2]);
|
||||
let ce = CreateEvent::new_internal(vec![e1, e2]);
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
|
@ -1739,19 +1749,19 @@ mod tests {
|
|||
}"#,
|
||||
)
|
||||
.expect("json failure");
|
||||
let ce = CreateEvent::from_vec(vec![e1]);
|
||||
let ce = CreateEvent::new_internal(vec![e1]);
|
||||
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
// Delete and ensure they became recycled.
|
||||
let de_sin = DeleteEvent::from_filter(Filter::Eq(
|
||||
let de_sin = DeleteEvent::new_internal(Filter::Eq(
|
||||
String::from("name"),
|
||||
String::from("testperson1"),
|
||||
));
|
||||
assert!(server_txn.delete(audit, &de_sin).is_ok());
|
||||
// Can in be seen by special search? (external recycle search)
|
||||
let filt_rc = Filter::Eq(String::from("class"), String::from("recycled"));
|
||||
let sre_rc = SearchEvent::new_rec_impersonate(filt_rc.clone());
|
||||
let sre_rc = SearchEvent::new_rec_impersonate_uuid(UUID_ADMIN, filt_rc.clone());
|
||||
let r2 = server_txn.search(audit, &sre_rc).expect("search failed");
|
||||
assert!(r2.len() == 1);
|
||||
|
||||
|
@ -1783,7 +1793,7 @@ mod tests {
|
|||
}"#,
|
||||
)
|
||||
.expect("json failure");
|
||||
let ce = CreateEvent::from_vec(vec![e1]);
|
||||
let ce = CreateEvent::new_internal(vec![e1]);
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
|
@ -1821,7 +1831,7 @@ mod tests {
|
|||
}"#,
|
||||
)
|
||||
.expect("json failure");
|
||||
let ce = CreateEvent::from_vec(vec![e1]);
|
||||
let ce = CreateEvent::new_internal(vec![e1]);
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
|
@ -1861,7 +1871,7 @@ mod tests {
|
|||
}"#,
|
||||
)
|
||||
.expect("json failure");
|
||||
let ce = CreateEvent::from_vec(vec![e1]);
|
||||
let ce = CreateEvent::new_internal(vec![e1]);
|
||||
let cr = server_txn.create(audit, &ce);
|
||||
assert!(cr.is_ok());
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ extern crate actix;
|
|||
|
||||
extern crate rsidm;
|
||||
|
||||
|
||||
use rsidm::config::Configuration;
|
||||
use rsidm::core::create_server_core;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use actix::prelude::*;
|
|||
|
||||
extern crate rsidm;
|
||||
use rsidm::config::Configuration;
|
||||
use rsidm::constants::UUID_ADMIN;
|
||||
use rsidm::core::create_server_core;
|
||||
use rsidm::proto_v1::{CreateRequest, Entry, OperationResponse};
|
||||
|
||||
|
@ -68,7 +69,10 @@ fn test_server_proto() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let c = CreateRequest { entries: vec![e] };
|
||||
let c = CreateRequest {
|
||||
entries: vec![e],
|
||||
user_uuid: UUID_ADMIN.to_string(),
|
||||
};
|
||||
|
||||
let mut response = client
|
||||
.post("http://127.0.0.1:8080/v1/create")
|
||||
|
|
Loading…
Reference in a new issue