mirror of
https://github.com/kanidm/kanidm.git
synced 2025-04-20 01:05:38 +02:00
Removes the protected plugin into an access control module so that it's outputs can be properly represented in effective access checks.
428 lines
16 KiB
Rust
428 lines
16 KiB
Rust
//! Plugins allow an `Event` to be inspected and transformed during the write
|
|
//! paths of the server. This allows richer expression of some concepts and
|
|
//! helps to ensure that data is always in specific known states within the
|
|
//! `QueryServer`
|
|
|
|
use std::collections::BTreeSet;
|
|
use std::sync::Arc;
|
|
|
|
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
|
|
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
|
use crate::prelude::*;
|
|
|
|
mod attrunique;
|
|
mod base;
|
|
mod cred_import;
|
|
mod default_values;
|
|
mod domain;
|
|
pub(crate) mod dyngroup;
|
|
mod eckeygen;
|
|
pub(crate) mod gidnumber;
|
|
mod jwskeygen;
|
|
mod keyobject;
|
|
mod memberof;
|
|
mod namehistory;
|
|
mod refint;
|
|
mod session;
|
|
mod spn;
|
|
mod valuedeny;
|
|
|
|
trait Plugin {
|
|
fn id() -> &'static str;
|
|
|
|
fn pre_create_transform(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &mut Vec<EntryInvalidNew>,
|
|
_ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented pre_create_transform!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn pre_create(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
// List of what we will commit that is valid?
|
|
_cand: &[EntrySealedNew],
|
|
_ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented pre_create!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_create(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
// List of what we committed that was valid?
|
|
_cand: &[EntrySealedCommitted],
|
|
_ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented post_create!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn pre_modify(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_pre_cand: &[Arc<EntrySealedCommitted>],
|
|
_cand: &mut Vec<EntryInvalidCommitted>,
|
|
_me: &ModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented pre_modify!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_modify(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
// List of what we modified that was valid?
|
|
_pre_cand: &[Arc<EntrySealedCommitted>],
|
|
_cand: &[EntrySealedCommitted],
|
|
_ce: &ModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented post_modify!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn pre_batch_modify(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_pre_cand: &[Arc<EntrySealedCommitted>],
|
|
_cand: &mut Vec<EntryInvalidCommitted>,
|
|
_me: &BatchModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented pre_batch_modify!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_batch_modify(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
// List of what we modified that was valid?
|
|
_pre_cand: &[Arc<EntrySealedCommitted>],
|
|
_cand: &[EntrySealedCommitted],
|
|
_me: &BatchModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented post_batch_modify!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn pre_delete(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &mut Vec<EntryInvalidCommitted>,
|
|
_de: &DeleteEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented pre_delete!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_delete(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
// List of what we delete that was valid?
|
|
_cand: &[EntrySealedCommitted],
|
|
_ce: &DeleteEvent,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!("plugin {} has an unimplemented post_delete!", Self::id());
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn pre_repl_refresh(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &[EntryRefreshNew],
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented pre_repl_refresh!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_repl_refresh(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &[EntrySealedCommitted],
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented post_repl_refresh!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
// fn pre_repl_incremental(
|
|
// _qs: &mut QueryServerWriteTransaction,
|
|
// _cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
|
|
// ) -> Result<(), OperationError> {
|
|
// admin_error!(
|
|
// "plugin {} has an unimplemented pre_repl_incremental!",
|
|
// Self::id()
|
|
// );
|
|
// debug_assert!(false);
|
|
// Err(OperationError::InvalidState)
|
|
// }
|
|
|
|
fn post_repl_incremental_conflict(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
|
|
_conflict_uuids: &mut BTreeSet<Uuid>,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented post_repl_incremental_conflict!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn post_repl_incremental(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_pre_cand: &[Arc<EntrySealedCommitted>],
|
|
_cand: &[EntrySealedCommitted],
|
|
_conflict_uuids: &BTreeSet<Uuid>,
|
|
) -> Result<(), OperationError> {
|
|
admin_error!(
|
|
"plugin {} has an unimplemented post_repl_incremental!",
|
|
Self::id()
|
|
);
|
|
debug_assert!(false);
|
|
Err(OperationError::InvalidState)
|
|
}
|
|
|
|
fn verify(_qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
|
|
admin_error!("plugin {} has an unimplemented verify!", Self::id());
|
|
vec![Err(ConsistencyError::Unknown)]
|
|
}
|
|
}
|
|
|
|
pub struct Plugins {}
|
|
|
|
macro_rules! run_verify_plugin {
|
|
(
|
|
$qs:ident,
|
|
$results:expr,
|
|
$target_plugin:ty
|
|
) => {{
|
|
let mut r = <$target_plugin>::verify($qs);
|
|
$results.append(&mut r);
|
|
}};
|
|
}
|
|
|
|
impl Plugins {
|
|
#[instrument(level = "debug", name = "plugins::run_pre_create_transform", skip_all)]
|
|
pub fn run_pre_create_transform(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
|
|
ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
base::Base::pre_create_transform(qs, cand, ce)?;
|
|
valuedeny::ValueDeny::pre_create_transform(qs, cand, ce)?;
|
|
cred_import::CredImport::pre_create_transform(qs, cand, ce)?;
|
|
keyobject::KeyObjectManagement::pre_create_transform(qs, cand, ce)?;
|
|
jwskeygen::JwsKeygen::pre_create_transform(qs, cand, ce)?;
|
|
gidnumber::GidNumber::pre_create_transform(qs, cand, ce)?;
|
|
domain::Domain::pre_create_transform(qs, cand, ce)?;
|
|
spn::Spn::pre_create_transform(qs, cand, ce)?;
|
|
default_values::DefaultValues::pre_create_transform(qs, cand, ce)?;
|
|
namehistory::NameHistory::pre_create_transform(qs, cand, ce)?;
|
|
eckeygen::EcdhKeyGen::pre_create_transform(qs, cand, ce)?;
|
|
// Should always be last
|
|
attrunique::AttrUnique::pre_create_transform(qs, cand, ce)
|
|
}
|
|
|
|
#[instrument(level = "trace", name = "plugins::run_pre_create", skip_all)]
|
|
pub fn run_pre_create(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &[Entry<EntrySealed, EntryNew>],
|
|
_ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
Ok(())
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_create", skip_all)]
|
|
pub fn run_post_create(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &[Entry<EntrySealed, EntryCommitted>],
|
|
ce: &CreateEvent,
|
|
) -> Result<(), OperationError> {
|
|
refint::ReferentialIntegrity::post_create(qs, cand, ce)?;
|
|
memberof::MemberOf::post_create(qs, cand, ce)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_pre_modify", skip_all)]
|
|
pub fn run_pre_modify(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
pre_cand: &[Arc<EntrySealedCommitted>],
|
|
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
|
me: &ModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
base::Base::pre_modify(qs, pre_cand, cand, me)?;
|
|
valuedeny::ValueDeny::pre_modify(qs, pre_cand, cand, me)?;
|
|
cred_import::CredImport::pre_modify(qs, pre_cand, cand, me)?;
|
|
jwskeygen::JwsKeygen::pre_modify(qs, pre_cand, cand, me)?;
|
|
keyobject::KeyObjectManagement::pre_modify(qs, pre_cand, cand, me)?;
|
|
gidnumber::GidNumber::pre_modify(qs, pre_cand, cand, me)?;
|
|
domain::Domain::pre_modify(qs, pre_cand, cand, me)?;
|
|
spn::Spn::pre_modify(qs, pre_cand, cand, me)?;
|
|
session::SessionConsistency::pre_modify(qs, pre_cand, cand, me)?;
|
|
default_values::DefaultValues::pre_modify(qs, pre_cand, cand, me)?;
|
|
namehistory::NameHistory::pre_modify(qs, pre_cand, cand, me)?;
|
|
eckeygen::EcdhKeyGen::pre_modify(qs, pre_cand, cand, me)?;
|
|
// attr unique should always be last
|
|
attrunique::AttrUnique::pre_modify(qs, pre_cand, cand, me)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_modify", skip_all)]
|
|
pub fn run_post_modify(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
|
|
cand: &[Entry<EntrySealed, EntryCommitted>],
|
|
me: &ModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
refint::ReferentialIntegrity::post_modify(qs, pre_cand, cand, me)?;
|
|
spn::Spn::post_modify(qs, pre_cand, cand, me)?;
|
|
memberof::MemberOf::post_modify(qs, pre_cand, cand, me)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_pre_batch_modify", skip_all)]
|
|
pub fn run_pre_batch_modify(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
pre_cand: &[Arc<EntrySealedCommitted>],
|
|
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
|
me: &BatchModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
base::Base::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
valuedeny::ValueDeny::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
cred_import::CredImport::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
jwskeygen::JwsKeygen::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
keyobject::KeyObjectManagement::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
gidnumber::GidNumber::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
domain::Domain::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
spn::Spn::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
session::SessionConsistency::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
default_values::DefaultValues::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
namehistory::NameHistory::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
eckeygen::EcdhKeyGen::pre_batch_modify(qs, pre_cand, cand, me)?;
|
|
// attr unique should always be last
|
|
attrunique::AttrUnique::pre_batch_modify(qs, pre_cand, cand, me)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_batch_modify", skip_all)]
|
|
pub fn run_post_batch_modify(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
|
|
cand: &[Entry<EntrySealed, EntryCommitted>],
|
|
me: &BatchModifyEvent,
|
|
) -> Result<(), OperationError> {
|
|
refint::ReferentialIntegrity::post_batch_modify(qs, pre_cand, cand, me)?;
|
|
spn::Spn::post_batch_modify(qs, pre_cand, cand, me)?;
|
|
memberof::MemberOf::post_batch_modify(qs, pre_cand, cand, me)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_pre_delete", skip_all)]
|
|
pub fn run_pre_delete(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
|
de: &DeleteEvent,
|
|
) -> Result<(), OperationError> {
|
|
memberof::MemberOf::pre_delete(qs, cand, de)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_delete", skip_all)]
|
|
pub fn run_post_delete(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &[Entry<EntrySealed, EntryCommitted>],
|
|
de: &DeleteEvent,
|
|
) -> Result<(), OperationError> {
|
|
refint::ReferentialIntegrity::post_delete(qs, cand, de)?;
|
|
memberof::MemberOf::post_delete(qs, cand, de)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_pre_repl_refresh", skip_all)]
|
|
pub fn run_pre_repl_refresh(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &[EntryRefreshNew],
|
|
) -> Result<(), OperationError> {
|
|
attrunique::AttrUnique::pre_repl_refresh(qs, cand)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_repl_refresh", skip_all)]
|
|
pub fn run_post_repl_refresh(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &[EntrySealedCommitted],
|
|
) -> Result<(), OperationError> {
|
|
refint::ReferentialIntegrity::post_repl_refresh(qs, cand)?;
|
|
memberof::MemberOf::post_repl_refresh(qs, cand)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_pre_repl_incremental", skip_all)]
|
|
pub fn run_pre_repl_incremental(
|
|
_qs: &mut QueryServerWriteTransaction,
|
|
_cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
|
|
) -> Result<(), OperationError> {
|
|
// Cleanup sessions on incoming replication? May not actually
|
|
// be needed since each node will be session checking and replicating
|
|
// those cleanups as needed.
|
|
// session::SessionConsistency::pre_repl_incremental(qs, cand)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[instrument(
|
|
level = "debug",
|
|
name = "plugins::run_post_repl_incremental_conflict",
|
|
skip_all
|
|
)]
|
|
pub fn run_post_repl_incremental_conflict(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
|
|
conflict_uuids: &mut BTreeSet<Uuid>,
|
|
) -> Result<(), OperationError> {
|
|
// Attr unique MUST BE FIRST.
|
|
attrunique::AttrUnique::post_repl_incremental_conflict(qs, cand, conflict_uuids)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_post_repl_incremental", skip_all)]
|
|
pub fn run_post_repl_incremental(
|
|
qs: &mut QueryServerWriteTransaction,
|
|
pre_cand: &[Arc<EntrySealedCommitted>],
|
|
cand: &[EntrySealedCommitted],
|
|
conflict_uuids: &BTreeSet<Uuid>,
|
|
) -> Result<(), OperationError> {
|
|
// Nothing to do yet.
|
|
// domain::Domain::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
|
|
spn::Spn::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
|
|
// refint MUST proceed memberof.
|
|
refint::ReferentialIntegrity::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
|
|
// Memberof MUST BE LAST.
|
|
memberof::MemberOf::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)
|
|
}
|
|
|
|
#[instrument(level = "debug", name = "plugins::run_verify", skip_all)]
|
|
pub fn run_verify(
|
|
qs: &mut QueryServerReadTransaction,
|
|
results: &mut Vec<Result<(), ConsistencyError>>,
|
|
) {
|
|
run_verify_plugin!(qs, results, base::Base);
|
|
run_verify_plugin!(qs, results, valuedeny::ValueDeny);
|
|
run_verify_plugin!(qs, results, attrunique::AttrUnique);
|
|
run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
|
|
run_verify_plugin!(qs, results, keyobject::KeyObjectManagement);
|
|
run_verify_plugin!(qs, results, dyngroup::DynGroup);
|
|
run_verify_plugin!(qs, results, memberof::MemberOf);
|
|
run_verify_plugin!(qs, results, spn::Spn);
|
|
}
|
|
}
|