Refactor client lib to expose msgs, and improve the messages in plugins (#114)

Implements #100. This refactors our error types to be deserialiseable, and exposes these through the clienterror type with the status codes. There is probably still a lot of improvements here to be made, but they'll be shaken out as the client libs develop I think and we start to see what errors should be exposed.
This commit is contained in:
Firstyear 2019-10-10 13:17:32 +10:00 committed by GitHub
parent 09bc31e2f5
commit 6b0b2ad040
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 187 additions and 128 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@
/insecure /insecure
**/*.rs.bk **/*.rs.bk
test.db test.db
/vendor

View file

@ -12,15 +12,15 @@ use std::io::Read;
use kanidm_proto::v1::{ use kanidm_proto::v1::{
AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep, CreateRequest, DeleteRequest, AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep, CreateRequest, DeleteRequest,
Entry, Filter, ModifyList, ModifyRequest, OperationResponse, SearchRequest, SearchResponse, Entry, Filter, ModifyList, ModifyRequest, OperationError, OperationResponse, SearchRequest,
SetAuthCredential, SingleStringRequest, UserAuthToken, WhoamiResponse, SearchResponse, SetAuthCredential, SingleStringRequest, UserAuthToken, WhoamiResponse,
}; };
use serde_json; use serde_json;
#[derive(Debug)] #[derive(Debug)]
pub enum ClientError { pub enum ClientError {
Unauthorized, Unauthorized,
Http(reqwest::StatusCode), Http(reqwest::StatusCode, Option<OperationError>),
Transport(reqwest::Error), Transport(reqwest::Error),
AuthenticationFailed, AuthenticationFailed,
JsonParse, JsonParse,
@ -100,7 +100,7 @@ impl KanidmClient {
match response.status() { match response.status() {
reqwest::StatusCode::OK => {} reqwest::StatusCode::OK => {}
unexpect => return Err(ClientError::Http(unexpect)), unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
} }
// TODO: What about errors // TODO: What about errors
@ -127,7 +127,7 @@ impl KanidmClient {
match response.status() { match response.status() {
reqwest::StatusCode::OK => {} reqwest::StatusCode::OK => {}
unexpect => return Err(ClientError::Http(unexpect)), unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
} }
// TODO: What about errors // TODO: What about errors
@ -146,7 +146,7 @@ impl KanidmClient {
match response.status() { match response.status() {
reqwest::StatusCode::OK => {} reqwest::StatusCode::OK => {}
unexpect => return Err(ClientError::Http(unexpect)), unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
} }
// TODO: What about errors // TODO: What about errors
@ -165,7 +165,7 @@ impl KanidmClient {
// Continue to process. // Continue to process.
reqwest::StatusCode::OK => {} reqwest::StatusCode::OK => {}
reqwest::StatusCode::UNAUTHORIZED => return Ok(None), reqwest::StatusCode::UNAUTHORIZED => return Ok(None),
unexpect => return Err(ClientError::Http(unexpect)), unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
} }
let r: WhoamiResponse = serde_json::from_str(response.text().unwrap().as_str()).unwrap(); let r: WhoamiResponse = serde_json::from_str(response.text().unwrap().as_str()).unwrap();

View file

@ -17,6 +17,13 @@ pub enum SchemaError {
Corrupted, Corrupted,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum PluginError {
AttrUnique(String),
Base(String),
ReferentialIntegrity(String),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum OperationError { pub enum OperationError {
EmptyRequest, EmptyRequest,
@ -25,20 +32,20 @@ pub enum OperationError {
CorruptedEntry(u64), CorruptedEntry(u64),
ConsistencyError(Vec<Result<(), ConsistencyError>>), ConsistencyError(Vec<Result<(), ConsistencyError>>),
SchemaViolation(SchemaError), SchemaViolation(SchemaError),
Plugin, Plugin(PluginError),
FilterGeneration, FilterGeneration,
FilterUUIDResolution, FilterUUIDResolution,
InvalidAttributeName(String), InvalidAttributeName(String),
InvalidAttribute(&'static str), InvalidAttribute(String),
InvalidDBState, InvalidDBState,
InvalidEntryID, InvalidEntryID,
InvalidRequestState, InvalidRequestState,
InvalidState, InvalidState,
InvalidEntryState, InvalidEntryState,
InvalidUuid, InvalidUuid,
InvalidACPState(&'static str), InvalidACPState(String),
InvalidSchemaState(&'static str), InvalidSchemaState(String),
InvalidAccountState(&'static str), InvalidAccountState(String),
BackendEngine, BackendEngine,
SQLiteError, //(RusqliteError) SQLiteError, //(RusqliteError)
FsError, FsError,
@ -46,9 +53,10 @@ pub enum OperationError {
SerdeCborError, SerdeCborError,
AccessDenied, AccessDenied,
NotAuthenticated, NotAuthenticated,
InvalidAuthState(&'static str), InvalidAuthState(String),
InvalidSessionState, InvalidSessionState,
SystemProtectedObject, SystemProtectedObject,
SystemProtectedAttribute,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
@ -62,7 +70,7 @@ pub enum ConsistencyError {
UuidNotUnique(String), UuidNotUnique(String),
RefintNotUpheld(u64), RefintNotUpheld(u64),
MemberOfInvalid(u64), MemberOfInvalid(u64),
InvalidAttributeType(&'static str), InvalidAttributeType(String),
DuplicateUniqueAttribute(String), DuplicateUniqueAttribute(String),
} }

View file

@ -58,7 +58,7 @@ impl AccessControlSearch {
if !value.attribute_value_pres("class", &CLASS_ACS) { if !value.attribute_value_pres("class", &CLASS_ACS) {
audit_log!(audit, "class access_control_search not present."); audit_log!(audit, "class access_control_search not present.");
return Err(OperationError::InvalidACPState( return Err(OperationError::InvalidACPState(
"Missing access_control_search", "Missing access_control_search".to_string(),
)); ));
} }
@ -66,7 +66,9 @@ impl AccessControlSearch {
audit, audit,
value value
.get_ava_string("acp_search_attr") .get_ava_string("acp_search_attr")
.ok_or(OperationError::InvalidACPState("Missing acp_search_attr")) .ok_or(OperationError::InvalidACPState(
"Missing acp_search_attr".to_string()
))
); );
let acp = AccessControlProfile::try_from(audit, qs, value)?; let acp = AccessControlProfile::try_from(audit, qs, value)?;
@ -111,7 +113,7 @@ impl AccessControlDelete {
if !value.attribute_value_pres("class", &CLASS_ACD) { if !value.attribute_value_pres("class", &CLASS_ACD) {
audit_log!(audit, "class access_control_delete not present."); audit_log!(audit, "class access_control_delete not present.");
return Err(OperationError::InvalidACPState( return Err(OperationError::InvalidACPState(
"Missing access_control_delete", "Missing access_control_delete".to_string(),
)); ));
} }
@ -154,7 +156,7 @@ impl AccessControlCreate {
if !value.attribute_value_pres("class", &CLASS_ACC) { if !value.attribute_value_pres("class", &CLASS_ACC) {
audit_log!(audit, "class access_control_create not present."); audit_log!(audit, "class access_control_create not present.");
return Err(OperationError::InvalidACPState( return Err(OperationError::InvalidACPState(
"Missing access_control_create", "Missing access_control_create".to_string(),
)); ));
} }
@ -212,7 +214,7 @@ impl AccessControlModify {
if !value.attribute_value_pres("class", &CLASS_ACM) { if !value.attribute_value_pres("class", &CLASS_ACM) {
audit_log!(audit, "class access_control_modify not present."); audit_log!(audit, "class access_control_modify not present.");
return Err(OperationError::InvalidACPState( return Err(OperationError::InvalidACPState(
"Missing access_control_modify", "Missing access_control_modify".to_string(),
)); ));
} }
@ -281,7 +283,7 @@ impl AccessControlProfile {
if !value.attribute_value_pres("class", &CLASS_ACP) { if !value.attribute_value_pres("class", &CLASS_ACP) {
audit_log!(audit, "class access_control_profile not present."); audit_log!(audit, "class access_control_profile not present.");
return Err(OperationError::InvalidACPState( return Err(OperationError::InvalidACPState(
"Missing access_control_profile", "Missing access_control_profile".to_string(),
)); ));
} }
@ -290,7 +292,7 @@ impl AccessControlProfile {
audit, audit,
value value
.get_ava_single_str("name") .get_ava_single_str("name")
.ok_or(OperationError::InvalidACPState("Missing name")) .ok_or(OperationError::InvalidACPState("Missing name".to_string()))
) )
.to_string(); .to_string();
// copy uuid // copy uuid
@ -298,16 +300,16 @@ impl AccessControlProfile {
// receiver, and turn to real filter // receiver, and turn to real filter
let receiver_f: ProtoFilter = try_audit!( let receiver_f: ProtoFilter = try_audit!(
audit, audit,
value value.get_ava_single_protofilter("acp_receiver").ok_or(
.get_ava_single_protofilter("acp_receiver") OperationError::InvalidACPState("Missing acp_receiver".to_string())
.ok_or(OperationError::InvalidACPState("Missing acp_receiver")) )
); );
// targetscope, and turn to real filter // targetscope, and turn to real filter
let targetscope_f: ProtoFilter = try_audit!( let targetscope_f: ProtoFilter = try_audit!(
audit, audit,
value value.get_ava_single_protofilter("acp_targetscope").ok_or(
.get_ava_single_protofilter("acp_targetscope") OperationError::InvalidACPState("Missing acp_targetscope".to_string())
.ok_or(OperationError::InvalidACPState("Missing acp_targetscope")) )
); );
let receiver_i = try_audit!(audit, Filter::from_rw(audit, &receiver_f, qs)); let receiver_i = try_audit!(audit, Filter::from_rw(audit, &receiver_f, qs));

View file

@ -55,6 +55,19 @@ fn get_current_user(req: &HttpRequest<AppState>) -> Option<UserAuthToken> {
} }
} }
fn operation_error_to_response(e: OperationError) -> HttpResponse {
match e {
OperationError::NotAuthenticated => HttpResponse::Unauthorized().json(e),
OperationError::AccessDenied | OperationError::SystemProtectedObject => {
HttpResponse::Forbidden().json(e)
}
OperationError::EmptyRequest
| OperationError::NoMatchingEntries
| OperationError::SchemaViolation(_) => HttpResponse::BadRequest().json(e),
_ => HttpResponse::InternalServerError().json(e),
}
}
macro_rules! json_event_post { macro_rules! json_event_post {
($req:expr, $state:expr, $message_type:ty, $request_type:ty) => {{ ($req:expr, $state:expr, $message_type:ty, $request_type:ty) => {{
// This is copied every request. Is there a better way? // This is copied every request. Is there a better way?
@ -100,7 +113,7 @@ macro_rules! json_event_post {
.from_err() .from_err()
.and_then(|res| match res { .and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)), Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(HttpResponse::InternalServerError().json(e)), Err(e) => Ok(operation_error_to_response(e)),
}); });
Box::new(res) Box::new(res)
@ -127,10 +140,7 @@ macro_rules! json_event_get {
let res = $state.qe.send(obj).from_err().and_then(|res| match res { let res = $state.qe.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)), Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => match e { Err(e) => Ok(operation_error_to_response(e)),
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
_ => Ok(HttpResponse::InternalServerError().json(e)),
},
}); });
Box::new(res) Box::new(res)
@ -184,10 +194,7 @@ fn json_rest_event_get(
let res = state.qe.send(obj).from_err().and_then(|res| match res { let res = state.qe.send(obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)), Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => match e { Err(e) => Ok(operation_error_to_response(e)),
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
_ => Ok(HttpResponse::InternalServerError().json(e)),
},
}); });
Box::new(res) Box::new(res)
@ -210,10 +217,7 @@ fn json_rest_event_get_id(
// Only send back the first result, or None // Only send back the first result, or None
Ok(HttpResponse::Ok().json(event_result.pop())) Ok(HttpResponse::Ok().json(event_result.pop()))
} }
Err(e) => match e { Err(e) => Ok(operation_error_to_response(e)),
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
_ => Ok(HttpResponse::InternalServerError().json(e)),
},
}); });
Box::new(res) Box::new(res)
@ -257,7 +261,7 @@ fn json_rest_event_credential_put(
let m_obj = InternalCredentialSetMessage::new(uat, id, cred_id, obj); let m_obj = InternalCredentialSetMessage::new(uat, id, cred_id, obj);
let res = state.qe.send(m_obj).from_err().and_then(|res| match res { let res = state.qe.send(m_obj).from_err().and_then(|res| match res {
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)), Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
Err(e) => Ok(HttpResponse::InternalServerError().json(e)), Err(e) => Ok(operation_error_to_response(e)),
}); });
Box::new(res) Box::new(res)
@ -321,10 +325,7 @@ fn schema_attributetype_get_id(
// Only send back the first result, or None // Only send back the first result, or None
Ok(HttpResponse::Ok().json(event_result.pop())) Ok(HttpResponse::Ok().json(event_result.pop()))
} }
Err(e) => match e { Err(e) => Ok(operation_error_to_response(e)),
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
_ => Ok(HttpResponse::InternalServerError().json(e)),
},
}); });
Box::new(res) Box::new(res)
@ -355,10 +356,7 @@ fn schema_classtype_get_id(
// Only send back the first result, or None // Only send back the first result, or None
Ok(HttpResponse::Ok().json(event_result.pop())) Ok(HttpResponse::Ok().json(event_result.pop()))
} }
Err(e) => match e { Err(e) => Ok(operation_error_to_response(e)),
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
_ => Ok(HttpResponse::InternalServerError().json(e)),
},
}); });
Box::new(res) Box::new(res)
@ -472,7 +470,7 @@ fn auth(
AuthState::Denied(_) => { AuthState::Denied(_) => {
// Remove the auth-session-id // Remove the auth-session-id
req.session().remove("auth-session-id"); req.session().remove("auth-session-id");
Ok(HttpResponse::Ok().json(ar)) Ok(HttpResponse::Unauthorized().json(ar))
} }
AuthState::Continue(_) => { AuthState::Continue(_) => {
// Ensure the auth-session-id is set // Ensure the auth-session-id is set
@ -489,7 +487,7 @@ fn auth(
} }
} }
} }
Err(e) => Ok(HttpResponse::InternalServerError().json(e)), Err(e) => Ok(operation_error_to_response(e)),
}); });
Box::new(res) Box::new(res)
} }

View file

@ -670,7 +670,7 @@ impl AuthEventStep {
AuthStep::Init(name, appid) => { AuthStep::Init(name, appid) => {
if sid.is_some() { if sid.is_some() {
Err(OperationError::InvalidAuthState( Err(OperationError::InvalidAuthState(
"session id present in init", "session id present in init".to_string(),
)) ))
} else { } else {
Ok(AuthEventStep::Init(AuthEventStepInit { Ok(AuthEventStep::Init(AuthEventStepInit {
@ -685,7 +685,7 @@ impl AuthEventStep {
creds: creds, creds: creds,
})), })),
None => Err(OperationError::InvalidAuthState( None => Err(OperationError::InvalidAuthState(
"session id not present in cred", "session id not present in cred".to_string(),
)), )),
}, },
} }

View file

@ -42,7 +42,7 @@ impl Account {
// Check the classes // Check the classes
if !value.attribute_value_pres("class", &PVCLASS_ACCOUNT) { if !value.attribute_value_pres("class", &PVCLASS_ACCOUNT) {
return Err(OperationError::InvalidAccountState( return Err(OperationError::InvalidAccountState(
"Missing class: account", "Missing class: account".to_string(),
)); ));
} }
@ -51,11 +51,11 @@ impl Account {
value value
.get_ava_single_string("name") .get_ava_single_string("name")
.ok_or(OperationError::InvalidAccountState( .ok_or(OperationError::InvalidAccountState(
"Missing attribute: name", "Missing attribute: name".to_string(),
))?; ))?;
let displayname = value.get_ava_single_string("displayname").ok_or( let displayname = value.get_ava_single_string("displayname").ok_or(
OperationError::InvalidAccountState("Missing attribute: displayname"), OperationError::InvalidAccountState("Missing attribute: displayname".to_string()),
)?; )?;
let primary = value let primary = value

View file

@ -199,7 +199,7 @@ impl AuthSession {
) -> Result<AuthState, OperationError> { ) -> Result<AuthState, OperationError> {
if self.finished { if self.finished {
return Err(OperationError::InvalidAuthState( return Err(OperationError::InvalidAuthState(
"session already finalised!", "session already finalised!".to_string(),
)); ));
} }

View file

@ -13,7 +13,7 @@ use crate::server::{
QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction, QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction,
}; };
use crate::value::PartialValue; use crate::value::PartialValue;
use kanidm_proto::v1::{ConsistencyError, OperationError}; use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -59,7 +59,9 @@ fn get_cand_attr_set<VALID, STATE>(
vr, vr,
uuid uuid
); );
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::AttrUnique(
"ava already exists".to_string(),
)));
} }
} }
} }
@ -109,7 +111,9 @@ fn enforce_unique<STATE>(
// If all okay, okay! // If all okay, okay!
if conflict_cand.len() > 0 { if conflict_cand.len() > 0 {
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::AttrUnique(
"duplicate value detected".to_string(),
)));
} }
Ok(()) Ok(())

View file

@ -13,7 +13,7 @@ use crate::server::{
QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction, QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction,
}; };
use crate::value::{PartialValue, Value}; use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError}; use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
lazy_static! { lazy_static! {
static ref CLASS_OBJECT: Value = Value::new_class("object"); static ref CLASS_OBJECT: Value = Value::new_class("object");
@ -66,7 +66,9 @@ impl Plugin for Base {
// Actually check we have a value, could be empty array ... // Actually check we have a value, could be empty array ...
if u.len() > 1 { if u.len() > 1 {
audit_log!(au, "Entry defines uuid attr, but multiple values."); audit_log!(au, "Entry defines uuid attr, but multiple values.");
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::Base(
"Uuid has multiple values".to_string(),
)));
}; };
// Schema of the value v, is checked in the filter generation. Neat! // Schema of the value v, is checked in the filter generation. Neat!
@ -78,7 +80,9 @@ impl Plugin for Base {
let v: Value = try_audit!( let v: Value = try_audit!(
au, au,
u.first() u.first()
.ok_or(OperationError::Plugin) .ok_or(OperationError::Plugin(PluginError::Base(
"Uuid format invalid".to_string()
)))
.map(|v| (*v).clone()) .map(|v| (*v).clone())
); );
v v
@ -104,14 +108,16 @@ impl Plugin for Base {
for entry in cand.iter() { for entry in cand.iter() {
let uuid_ref: &Uuid = entry let uuid_ref: &Uuid = entry
.get_ava_single("uuid") .get_ava_single("uuid")
.ok_or(OperationError::Plugin)? .ok_or(OperationError::InvalidEntryState)?
.to_uuid() .to_uuid()
.ok_or(OperationError::Plugin)?; .ok_or(OperationError::InvalidAttribute("uuid".to_string()))?;
audit_log!(au, "Entry valid UUID: {:?}", entry); audit_log!(au, "Entry valid UUID: {:?}", entry);
match cand_uuid.insert(uuid_ref) { match cand_uuid.insert(uuid_ref) {
false => { false => {
audit_log!(au, "uuid duplicate found in create set! {:?}", uuid_ref); audit_log!(au, "uuid duplicate found in create set! {:?}", uuid_ref);
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::Base(
"Uuid duplicate detected in request".to_string(),
)));
} }
true => {} true => {}
} }
@ -137,7 +143,9 @@ impl Plugin for Base {
"uuid from protected system UUID range found in create set! {:?}", "uuid from protected system UUID range found in create set! {:?}",
overlap overlap
); );
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::Base(
"Uuid must not be in protected range".to_string(),
)));
} }
} }
@ -147,7 +155,9 @@ impl Plugin for Base {
"uuid \"does not exist\" found in create set! {:?}", "uuid \"does not exist\" found in create set! {:?}",
uuid_does_not_exist uuid_does_not_exist
); );
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::Base(
"UUID_DOES_NOT_EXIST may not exist!".to_string(),
)));
} }
// Now from each element, generate a filter to search for all of them // Now from each element, generate a filter to search for all of them
@ -175,12 +185,14 @@ impl Plugin for Base {
Ok(b) => { Ok(b) => {
if b == true { if b == true {
audit_log!(au, "A UUID already exists, rejecting."); audit_log!(au, "A UUID already exists, rejecting.");
return Err(OperationError::Plugin); return Err(OperationError::Plugin(PluginError::Base(
"Uuid duplicate found in database".to_string(),
)));
} }
} }
Err(e) => { Err(e) => {
audit_log!(au, "Error occured checking UUID existance. {:?}", e); audit_log!(au, "Error occured checking UUID existance. {:?}", e);
return Err(OperationError::Plugin); return Err(e);
} }
} }
@ -201,7 +213,7 @@ impl Plugin for Base {
}; };
if attr == "uuid" { if attr == "uuid" {
audit_log!(au, "Modifications to UUID's are NOT ALLOWED"); audit_log!(au, "Modifications to UUID's are NOT ALLOWED");
return Err(OperationError::Plugin); return Err(OperationError::SystemProtectedAttribute);
} }
} }
Ok(()) Ok(())

View file

@ -28,7 +28,7 @@ trait Plugin {
"plugin {} has an unimplemented pre_create_transform!", "plugin {} has an unimplemented pre_create_transform!",
Self::id() Self::id()
); );
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn pre_create( fn pre_create(
@ -39,7 +39,7 @@ trait Plugin {
_ce: &CreateEvent, _ce: &CreateEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented pre_create!", Self::id()); debug!("plugin {} has an unimplemented pre_create!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn post_create( fn post_create(
@ -50,7 +50,7 @@ trait Plugin {
_ce: &CreateEvent, _ce: &CreateEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented post_create!", Self::id()); debug!("plugin {} has an unimplemented post_create!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn pre_modify( fn pre_modify(
@ -60,7 +60,7 @@ trait Plugin {
_me: &ModifyEvent, _me: &ModifyEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented pre_modify!", Self::id()); debug!("plugin {} has an unimplemented pre_modify!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn post_modify( fn post_modify(
@ -72,7 +72,7 @@ trait Plugin {
_ce: &ModifyEvent, _ce: &ModifyEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented post_modify!", Self::id()); debug!("plugin {} has an unimplemented post_modify!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn pre_delete( fn pre_delete(
@ -82,7 +82,7 @@ trait Plugin {
_de: &DeleteEvent, _de: &DeleteEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented pre_delete!", Self::id()); debug!("plugin {} has an unimplemented pre_delete!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn post_delete( fn post_delete(
@ -93,7 +93,7 @@ trait Plugin {
_ce: &DeleteEvent, _ce: &DeleteEvent,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("plugin {} has an unimplemented post_delete!", Self::id()); debug!("plugin {} has an unimplemented post_delete!", Self::id());
Err(OperationError::Plugin) Err(OperationError::InvalidState)
} }
fn verify( fn verify(

View file

@ -20,7 +20,7 @@ use crate::schema::SchemaTransaction;
use crate::server::QueryServerTransaction; use crate::server::QueryServerTransaction;
use crate::server::{QueryServerReadTransaction, QueryServerWriteTransaction}; use crate::server::{QueryServerReadTransaction, QueryServerWriteTransaction};
use crate::value::{PartialValue, Value}; use crate::value::{PartialValue, Value};
use kanidm_proto::v1::{ConsistencyError, OperationError}; use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
use uuid::Uuid; use uuid::Uuid;
// NOTE: This *must* be after base.rs!!! // NOTE: This *must* be after base.rs!!!
@ -35,7 +35,14 @@ impl ReferentialIntegrity {
uuid_value: &Value, uuid_value: &Value,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
debug!("{:?}", uuid_value); debug!("{:?}", uuid_value);
let uuid = try_audit!(au, uuid_value.to_ref_uuid().ok_or(OperationError::Plugin)); let uuid = try_audit!(
au,
uuid_value
.to_ref_uuid()
.ok_or(OperationError::InvalidAttribute(
"uuid could not become reference value".to_string()
))
);
let mut au_qs = AuditScope::new("qs_exist"); let mut au_qs = AuditScope::new("qs_exist");
// NOTE: This only checks LIVE entries (not using filter_all) // NOTE: This only checks LIVE entries (not using filter_all)
let filt_in = filter!(f_eq("uuid", PartialValue::new_uuid(uuid.clone()))); let filt_in = filter!(f_eq("uuid", PartialValue::new_uuid(uuid.clone())));
@ -53,7 +60,9 @@ impl ReferentialIntegrity {
rtype, rtype,
uuid uuid
); );
Err(OperationError::Plugin) Err(OperationError::Plugin(PluginError::ReferentialIntegrity(
"Uuid referenced not found in database".to_string(),
)))
} }
} }
} }
@ -225,7 +234,7 @@ impl Plugin for ReferentialIntegrity {
} }
} }
None => res.push(Err(ConsistencyError::InvalidAttributeType( None => res.push(Err(ConsistencyError::InvalidAttributeType(
"A non-value-ref type was found.", "A non-value-ref type was found.".to_string(),
))), ))),
} }
} }

View file

@ -47,25 +47,29 @@ impl SchemaAttribute {
// class // class
if !value.attribute_value_pres("class", &PVCLASS_ATTRIBUTETYPE) { if !value.attribute_value_pres("class", &PVCLASS_ATTRIBUTETYPE) {
audit_log!(audit, "class attribute type not present"); audit_log!(audit, "class attribute type not present");
return Err(OperationError::InvalidSchemaState("missing attributetype")); return Err(OperationError::InvalidSchemaState(
"missing attributetype".to_string(),
));
} }
// uuid // uuid
let uuid = value.get_uuid().clone(); let uuid = value.get_uuid().clone();
// name // name
let name = try_audit!( let name =
try_audit!(
audit, audit,
value value.get_ava_single_string("attributename").ok_or(
.get_ava_single_string("attributename") OperationError::InvalidSchemaState("missing attributename".to_string())
.ok_or(OperationError::InvalidSchemaState("missing attributename")) )
); );
// description // description
let description = try_audit!( let description =
try_audit!(
audit, audit,
value value.get_ava_single_string("description").ok_or(
.get_ava_single_string("description") OperationError::InvalidSchemaState("missing description".to_string())
.ok_or(OperationError::InvalidSchemaState("missing description")) )
); );
// multivalue // multivalue
@ -73,13 +77,17 @@ impl SchemaAttribute {
audit, audit,
value value
.get_ava_single_bool("multivalue") .get_ava_single_bool("multivalue")
.ok_or(OperationError::InvalidSchemaState("missing multivalue")) .ok_or(OperationError::InvalidSchemaState(
"missing multivalue".to_string()
))
); );
let unique = try_audit!( let unique = try_audit!(
audit, audit,
value value
.get_ava_single_bool("unique") .get_ava_single_bool("unique")
.ok_or(OperationError::InvalidSchemaState("missing unique")) .ok_or(OperationError::InvalidSchemaState(
"missing unique".to_string()
))
); );
// index vec // index vec
// even if empty, it SHOULD be present ... (is that value to put an empty set?) // even if empty, it SHOULD be present ... (is that value to put an empty set?)
@ -92,7 +100,7 @@ impl SchemaAttribute {
.into_iter() .into_iter()
.map(|v: &IndexType| v.clone()) .map(|v: &IndexType| v.clone())
.collect())) .collect()))
.map_err(|_| OperationError::InvalidSchemaState("Invalid index")) .map_err(|_| OperationError::InvalidSchemaState("Invalid index".to_string()))
); );
// syntax type // syntax type
let syntax = try_audit!( let syntax = try_audit!(
@ -100,7 +108,9 @@ impl SchemaAttribute {
value value
.get_ava_single_syntax("syntax") .get_ava_single_syntax("syntax")
.and_then(|s: &SyntaxType| Some(s.clone())) .and_then(|s: &SyntaxType| Some(s.clone()))
.ok_or(OperationError::InvalidSchemaState("missing syntax")) .ok_or(OperationError::InvalidSchemaState(
"missing syntax".to_string()
))
); );
Ok(SchemaAttribute { Ok(SchemaAttribute {
@ -339,7 +349,9 @@ impl SchemaClass {
// Convert entry to a schema class. // Convert entry to a schema class.
if !value.attribute_value_pres("class", &PVCLASS_CLASSTYPE) { if !value.attribute_value_pres("class", &PVCLASS_CLASSTYPE) {
audit_log!(audit, "class classtype not present"); audit_log!(audit, "class classtype not present");
return Err(OperationError::InvalidSchemaState("missing classtype")); return Err(OperationError::InvalidSchemaState(
"missing classtype".to_string(),
));
} }
// uuid // uuid
@ -350,14 +362,17 @@ impl SchemaClass {
audit, audit,
value value
.get_ava_single_string("classname") .get_ava_single_string("classname")
.ok_or(OperationError::InvalidSchemaState("missing classname")) .ok_or(OperationError::InvalidSchemaState(
"missing classname".to_string()
))
); );
// description // description
let description = try_audit!( let description =
try_audit!(
audit, audit,
value value.get_ava_single_string("description").ok_or(
.get_ava_single_string("description") OperationError::InvalidSchemaState("missing description".to_string())
.ok_or(OperationError::InvalidSchemaState("missing description")) )
); );
// These are all "optional" lists of strings. // These are all "optional" lists of strings.
@ -365,21 +380,23 @@ impl SchemaClass {
value value
.get_ava_opt_string("systemmay") .get_ava_opt_string("systemmay")
.ok_or(OperationError::InvalidSchemaState( .ok_or(OperationError::InvalidSchemaState(
"Missing or invalid systemmay", "Missing or invalid systemmay".to_string(),
))?; ))?;
let systemmust = let systemmust =
value value
.get_ava_opt_string("systemmust") .get_ava_opt_string("systemmust")
.ok_or(OperationError::InvalidSchemaState( .ok_or(OperationError::InvalidSchemaState(
"Missing or invalid systemmust", "Missing or invalid systemmust".to_string(),
))?; ))?;
let may = value let may = value
.get_ava_opt_string("may") .get_ava_opt_string("may")
.ok_or(OperationError::InvalidSchemaState("Missing or invalid may"))?; .ok_or(OperationError::InvalidSchemaState(
"Missing or invalid may".to_string(),
))?;
let must = value let must = value
.get_ava_opt_string("must") .get_ava_opt_string("must")
.ok_or(OperationError::InvalidSchemaState( .ok_or(OperationError::InvalidSchemaState(
"Missing or invalid must", "Missing or invalid must".to_string(),
))?; ))?;
Ok(SchemaClass { Ok(SchemaClass {

View file

@ -364,11 +364,11 @@ pub trait QueryServerTransaction {
SyntaxType::UTF8STRING => Ok(Value::new_utf8(value.clone())), SyntaxType::UTF8STRING => Ok(Value::new_utf8(value.clone())),
SyntaxType::UTF8STRING_INSENSITIVE => Ok(Value::new_iutf8s(value.as_str())), SyntaxType::UTF8STRING_INSENSITIVE => Ok(Value::new_iutf8s(value.as_str())),
SyntaxType::BOOLEAN => Value::new_bools(value.as_str()) SyntaxType::BOOLEAN => Value::new_bools(value.as_str())
.ok_or(OperationError::InvalidAttribute("Invalid boolean syntax")), .ok_or(OperationError::InvalidAttribute("Invalid boolean syntax".to_string())),
SyntaxType::SYNTAX_ID => Value::new_syntaxs(value.as_str()) SyntaxType::SYNTAX_ID => Value::new_syntaxs(value.as_str())
.ok_or(OperationError::InvalidAttribute("Invalid Syntax syntax")), .ok_or(OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())),
SyntaxType::INDEX_ID => Value::new_indexs(value.as_str()) SyntaxType::INDEX_ID => Value::new_indexs(value.as_str())
.ok_or(OperationError::InvalidAttribute("Invalid Index syntax")), .ok_or(OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
SyntaxType::UUID => { SyntaxType::UUID => {
// It's a uuid - we do NOT check for existance, because that // It's a uuid - we do NOT check for existance, because that
// could be revealing or disclosing - it is up to acp to assert // could be revealing or disclosing - it is up to acp to assert
@ -386,7 +386,7 @@ pub trait QueryServerTransaction {
Some(Value::new_uuid(un)) Some(Value::new_uuid(un))
}) })
// I think this is unreachable due to how the .or_else works. // I think this is unreachable due to how the .or_else works.
.ok_or(OperationError::InvalidAttribute("Invalid UUID syntax")) .ok_or(OperationError::InvalidAttribute("Invalid UUID syntax".to_string()))
} }
SyntaxType::REFERENCE_UUID => { SyntaxType::REFERENCE_UUID => {
// See comments above. // See comments above.
@ -398,11 +398,11 @@ pub trait QueryServerTransaction {
Some(Value::new_refer(un)) Some(Value::new_refer(un))
}) })
// I think this is unreachable due to how the .or_else works. // I think this is unreachable due to how the .or_else works.
.ok_or(OperationError::InvalidAttribute("Invalid Reference syntax")) .ok_or(OperationError::InvalidAttribute("Invalid Reference syntax".to_string()))
} }
SyntaxType::JSON_FILTER => Value::new_json_filter(value) SyntaxType::JSON_FILTER => Value::new_json_filter(value)
.ok_or(OperationError::InvalidAttribute("Invalid Filter syntax")), .ok_or(OperationError::InvalidAttribute("Invalid Filter syntax".to_string())),
SyntaxType::CREDENTIAL => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api")), SyntaxType::CREDENTIAL => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
} }
} }
None => { None => {
@ -431,12 +431,15 @@ pub trait QueryServerTransaction {
SyntaxType::UTF8STRING_INSENSITIVE => { SyntaxType::UTF8STRING_INSENSITIVE => {
Ok(PartialValue::new_iutf8s(value.as_str())) Ok(PartialValue::new_iutf8s(value.as_str()))
} }
SyntaxType::BOOLEAN => PartialValue::new_bools(value.as_str()) SyntaxType::BOOLEAN => PartialValue::new_bools(value.as_str()).ok_or(
.ok_or(OperationError::InvalidAttribute("Invalid boolean syntax")), OperationError::InvalidAttribute("Invalid boolean syntax".to_string()),
SyntaxType::SYNTAX_ID => PartialValue::new_syntaxs(value.as_str()) ),
.ok_or(OperationError::InvalidAttribute("Invalid Syntax syntax")), SyntaxType::SYNTAX_ID => PartialValue::new_syntaxs(value.as_str()).ok_or(
SyntaxType::INDEX_ID => PartialValue::new_indexs(value.as_str()) OperationError::InvalidAttribute("Invalid Syntax syntax".to_string()),
.ok_or(OperationError::InvalidAttribute("Invalid Index syntax")), ),
SyntaxType::INDEX_ID => PartialValue::new_indexs(value.as_str()).ok_or(
OperationError::InvalidAttribute("Invalid Index syntax".to_string()),
),
SyntaxType::UUID => { SyntaxType::UUID => {
PartialValue::new_uuids(value.as_str()) PartialValue::new_uuids(value.as_str())
.or_else(|| { .or_else(|| {
@ -450,7 +453,9 @@ pub trait QueryServerTransaction {
Some(PartialValue::new_uuid(un)) Some(PartialValue::new_uuid(un))
}) })
// I think this is unreachable due to how the .or_else works. // I think this is unreachable due to how the .or_else works.
.ok_or(OperationError::InvalidAttribute("Invalid UUID syntax")) .ok_or(OperationError::InvalidAttribute(
"Invalid UUID syntax".to_string(),
))
} }
SyntaxType::REFERENCE_UUID => { SyntaxType::REFERENCE_UUID => {
// See comments above. // See comments above.
@ -462,10 +467,13 @@ pub trait QueryServerTransaction {
Some(PartialValue::new_refer(un)) Some(PartialValue::new_refer(un))
}) })
// I think this is unreachable due to how the .or_else works. // I think this is unreachable due to how the .or_else works.
.ok_or(OperationError::InvalidAttribute("Invalid Reference syntax")) .ok_or(OperationError::InvalidAttribute(
"Invalid Reference syntax".to_string(),
))
} }
SyntaxType::JSON_FILTER => PartialValue::new_json_filter(value) SyntaxType::JSON_FILTER => PartialValue::new_json_filter(value).ok_or(
.ok_or(OperationError::InvalidAttribute("Invalid Filter syntax")), OperationError::InvalidAttribute("Invalid Filter syntax".to_string()),
),
SyntaxType::CREDENTIAL => Ok(PartialValue::new_credential_tag(value.as_str())), SyntaxType::CREDENTIAL => Ok(PartialValue::new_credential_tag(value.as_str())),
} }
} }