mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 04:27:02 +01:00
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:
parent
09bc31e2f5
commit
6b0b2ad040
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@
|
|||
/insecure
|
||||
**/*.rs.bk
|
||||
test.db
|
||||
/vendor
|
||||
|
|
|
@ -12,15 +12,15 @@ use std::io::Read;
|
|||
|
||||
use kanidm_proto::v1::{
|
||||
AuthCredential, AuthRequest, AuthResponse, AuthState, AuthStep, CreateRequest, DeleteRequest,
|
||||
Entry, Filter, ModifyList, ModifyRequest, OperationResponse, SearchRequest, SearchResponse,
|
||||
SetAuthCredential, SingleStringRequest, UserAuthToken, WhoamiResponse,
|
||||
Entry, Filter, ModifyList, ModifyRequest, OperationError, OperationResponse, SearchRequest,
|
||||
SearchResponse, SetAuthCredential, SingleStringRequest, UserAuthToken, WhoamiResponse,
|
||||
};
|
||||
use serde_json;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientError {
|
||||
Unauthorized,
|
||||
Http(reqwest::StatusCode),
|
||||
Http(reqwest::StatusCode, Option<OperationError>),
|
||||
Transport(reqwest::Error),
|
||||
AuthenticationFailed,
|
||||
JsonParse,
|
||||
|
@ -100,7 +100,7 @@ impl KanidmClient {
|
|||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => {}
|
||||
unexpect => return Err(ClientError::Http(unexpect)),
|
||||
unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
|
||||
}
|
||||
|
||||
// TODO: What about errors
|
||||
|
@ -127,7 +127,7 @@ impl KanidmClient {
|
|||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => {}
|
||||
unexpect => return Err(ClientError::Http(unexpect)),
|
||||
unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
|
||||
}
|
||||
|
||||
// TODO: What about errors
|
||||
|
@ -146,7 +146,7 @@ impl KanidmClient {
|
|||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => {}
|
||||
unexpect => return Err(ClientError::Http(unexpect)),
|
||||
unexpect => return Err(ClientError::Http(unexpect, response.json().ok())),
|
||||
}
|
||||
|
||||
// TODO: What about errors
|
||||
|
@ -165,7 +165,7 @@ impl KanidmClient {
|
|||
// Continue to process.
|
||||
reqwest::StatusCode::OK => {}
|
||||
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();
|
||||
|
|
|
@ -17,6 +17,13 @@ pub enum SchemaError {
|
|||
Corrupted,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub enum PluginError {
|
||||
AttrUnique(String),
|
||||
Base(String),
|
||||
ReferentialIntegrity(String),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub enum OperationError {
|
||||
EmptyRequest,
|
||||
|
@ -25,20 +32,20 @@ pub enum OperationError {
|
|||
CorruptedEntry(u64),
|
||||
ConsistencyError(Vec<Result<(), ConsistencyError>>),
|
||||
SchemaViolation(SchemaError),
|
||||
Plugin,
|
||||
Plugin(PluginError),
|
||||
FilterGeneration,
|
||||
FilterUUIDResolution,
|
||||
InvalidAttributeName(String),
|
||||
InvalidAttribute(&'static str),
|
||||
InvalidAttribute(String),
|
||||
InvalidDBState,
|
||||
InvalidEntryID,
|
||||
InvalidRequestState,
|
||||
InvalidState,
|
||||
InvalidEntryState,
|
||||
InvalidUuid,
|
||||
InvalidACPState(&'static str),
|
||||
InvalidSchemaState(&'static str),
|
||||
InvalidAccountState(&'static str),
|
||||
InvalidACPState(String),
|
||||
InvalidSchemaState(String),
|
||||
InvalidAccountState(String),
|
||||
BackendEngine,
|
||||
SQLiteError, //(RusqliteError)
|
||||
FsError,
|
||||
|
@ -46,9 +53,10 @@ pub enum OperationError {
|
|||
SerdeCborError,
|
||||
AccessDenied,
|
||||
NotAuthenticated,
|
||||
InvalidAuthState(&'static str),
|
||||
InvalidAuthState(String),
|
||||
InvalidSessionState,
|
||||
SystemProtectedObject,
|
||||
SystemProtectedAttribute,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
|
@ -62,7 +70,7 @@ pub enum ConsistencyError {
|
|||
UuidNotUnique(String),
|
||||
RefintNotUpheld(u64),
|
||||
MemberOfInvalid(u64),
|
||||
InvalidAttributeType(&'static str),
|
||||
InvalidAttributeType(String),
|
||||
DuplicateUniqueAttribute(String),
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ impl AccessControlSearch {
|
|||
if !value.attribute_value_pres("class", &CLASS_ACS) {
|
||||
audit_log!(audit, "class access_control_search not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
"Missing access_control_search",
|
||||
"Missing access_control_search".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,9 @@ impl AccessControlSearch {
|
|||
audit,
|
||||
value
|
||||
.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)?;
|
||||
|
@ -111,7 +113,7 @@ impl AccessControlDelete {
|
|||
if !value.attribute_value_pres("class", &CLASS_ACD) {
|
||||
audit_log!(audit, "class access_control_delete not present.");
|
||||
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) {
|
||||
audit_log!(audit, "class access_control_create not present.");
|
||||
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) {
|
||||
audit_log!(audit, "class access_control_modify not present.");
|
||||
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) {
|
||||
audit_log!(audit, "class access_control_profile not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
"Missing access_control_profile",
|
||||
"Missing access_control_profile".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -290,7 +292,7 @@ impl AccessControlProfile {
|
|||
audit,
|
||||
value
|
||||
.get_ava_single_str("name")
|
||||
.ok_or(OperationError::InvalidACPState("Missing name"))
|
||||
.ok_or(OperationError::InvalidACPState("Missing name".to_string()))
|
||||
)
|
||||
.to_string();
|
||||
// copy uuid
|
||||
|
@ -298,16 +300,16 @@ impl AccessControlProfile {
|
|||
// receiver, and turn to real filter
|
||||
let receiver_f: ProtoFilter = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_protofilter("acp_receiver")
|
||||
.ok_or(OperationError::InvalidACPState("Missing acp_receiver"))
|
||||
value.get_ava_single_protofilter("acp_receiver").ok_or(
|
||||
OperationError::InvalidACPState("Missing acp_receiver".to_string())
|
||||
)
|
||||
);
|
||||
// targetscope, and turn to real filter
|
||||
let targetscope_f: ProtoFilter = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_protofilter("acp_targetscope")
|
||||
.ok_or(OperationError::InvalidACPState("Missing acp_targetscope"))
|
||||
value.get_ava_single_protofilter("acp_targetscope").ok_or(
|
||||
OperationError::InvalidACPState("Missing acp_targetscope".to_string())
|
||||
)
|
||||
);
|
||||
|
||||
let receiver_i = try_audit!(audit, Filter::from_rw(audit, &receiver_f, qs));
|
||||
|
|
|
@ -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 {
|
||||
($req:expr, $state:expr, $message_type:ty, $request_type:ty) => {{
|
||||
// This is copied every request. Is there a better way?
|
||||
|
@ -100,7 +113,7 @@ macro_rules! json_event_post {
|
|||
.from_err()
|
||||
.and_then(|res| match res {
|
||||
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)
|
||||
|
@ -127,10 +140,7 @@ macro_rules! json_event_get {
|
|||
|
||||
let res = $state.qe.send(obj).from_err().and_then(|res| match res {
|
||||
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
|
||||
Err(e) => match e {
|
||||
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
|
||||
_ => Ok(HttpResponse::InternalServerError().json(e)),
|
||||
},
|
||||
Err(e) => Ok(operation_error_to_response(e)),
|
||||
});
|
||||
|
||||
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 {
|
||||
Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)),
|
||||
Err(e) => match e {
|
||||
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
|
||||
_ => Ok(HttpResponse::InternalServerError().json(e)),
|
||||
},
|
||||
Err(e) => Ok(operation_error_to_response(e)),
|
||||
});
|
||||
|
||||
Box::new(res)
|
||||
|
@ -210,10 +217,7 @@ fn json_rest_event_get_id(
|
|||
// Only send back the first result, or None
|
||||
Ok(HttpResponse::Ok().json(event_result.pop()))
|
||||
}
|
||||
Err(e) => match e {
|
||||
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
|
||||
_ => Ok(HttpResponse::InternalServerError().json(e)),
|
||||
},
|
||||
Err(e) => Ok(operation_error_to_response(e)),
|
||||
});
|
||||
|
||||
Box::new(res)
|
||||
|
@ -257,7 +261,7 @@ fn json_rest_event_credential_put(
|
|||
let m_obj = InternalCredentialSetMessage::new(uat, id, cred_id, obj);
|
||||
let res = state.qe.send(m_obj).from_err().and_then(|res| match res {
|
||||
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)
|
||||
|
@ -321,10 +325,7 @@ fn schema_attributetype_get_id(
|
|||
// Only send back the first result, or None
|
||||
Ok(HttpResponse::Ok().json(event_result.pop()))
|
||||
}
|
||||
Err(e) => match e {
|
||||
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
|
||||
_ => Ok(HttpResponse::InternalServerError().json(e)),
|
||||
},
|
||||
Err(e) => Ok(operation_error_to_response(e)),
|
||||
});
|
||||
|
||||
Box::new(res)
|
||||
|
@ -355,10 +356,7 @@ fn schema_classtype_get_id(
|
|||
// Only send back the first result, or None
|
||||
Ok(HttpResponse::Ok().json(event_result.pop()))
|
||||
}
|
||||
Err(e) => match e {
|
||||
OperationError::NotAuthenticated => Ok(HttpResponse::Unauthorized().json(e)),
|
||||
_ => Ok(HttpResponse::InternalServerError().json(e)),
|
||||
},
|
||||
Err(e) => Ok(operation_error_to_response(e)),
|
||||
});
|
||||
|
||||
Box::new(res)
|
||||
|
@ -472,7 +470,7 @@ fn auth(
|
|||
AuthState::Denied(_) => {
|
||||
// Remove the auth-session-id
|
||||
req.session().remove("auth-session-id");
|
||||
Ok(HttpResponse::Ok().json(ar))
|
||||
Ok(HttpResponse::Unauthorized().json(ar))
|
||||
}
|
||||
AuthState::Continue(_) => {
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -670,7 +670,7 @@ impl AuthEventStep {
|
|||
AuthStep::Init(name, appid) => {
|
||||
if sid.is_some() {
|
||||
Err(OperationError::InvalidAuthState(
|
||||
"session id present in init",
|
||||
"session id present in init".to_string(),
|
||||
))
|
||||
} else {
|
||||
Ok(AuthEventStep::Init(AuthEventStepInit {
|
||||
|
@ -685,7 +685,7 @@ impl AuthEventStep {
|
|||
creds: creds,
|
||||
})),
|
||||
None => Err(OperationError::InvalidAuthState(
|
||||
"session id not present in cred",
|
||||
"session id not present in cred".to_string(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ impl Account {
|
|||
// Check the classes
|
||||
if !value.attribute_value_pres("class", &PVCLASS_ACCOUNT) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account",
|
||||
"Missing class: account".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -51,11 +51,11 @@ impl Account {
|
|||
value
|
||||
.get_ava_single_string("name")
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: name",
|
||||
"Missing attribute: name".to_string(),
|
||||
))?;
|
||||
|
||||
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
|
||||
|
|
|
@ -199,7 +199,7 @@ impl AuthSession {
|
|||
) -> Result<AuthState, OperationError> {
|
||||
if self.finished {
|
||||
return Err(OperationError::InvalidAuthState(
|
||||
"session already finalised!",
|
||||
"session already finalised!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::server::{
|
|||
QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction,
|
||||
};
|
||||
use crate::value::PartialValue;
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
@ -59,7 +59,9 @@ fn get_cand_attr_set<VALID, STATE>(
|
|||
vr,
|
||||
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 conflict_cand.len() > 0 {
|
||||
return Err(OperationError::Plugin);
|
||||
return Err(OperationError::Plugin(PluginError::AttrUnique(
|
||||
"duplicate value detected".to_string(),
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::server::{
|
|||
QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction,
|
||||
};
|
||||
use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
|
||||
|
||||
lazy_static! {
|
||||
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 ...
|
||||
if u.len() > 1 {
|
||||
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!
|
||||
|
@ -78,7 +80,9 @@ impl Plugin for Base {
|
|||
let v: Value = try_audit!(
|
||||
au,
|
||||
u.first()
|
||||
.ok_or(OperationError::Plugin)
|
||||
.ok_or(OperationError::Plugin(PluginError::Base(
|
||||
"Uuid format invalid".to_string()
|
||||
)))
|
||||
.map(|v| (*v).clone())
|
||||
);
|
||||
v
|
||||
|
@ -104,14 +108,16 @@ impl Plugin for Base {
|
|||
for entry in cand.iter() {
|
||||
let uuid_ref: &Uuid = entry
|
||||
.get_ava_single("uuid")
|
||||
.ok_or(OperationError::Plugin)?
|
||||
.ok_or(OperationError::InvalidEntryState)?
|
||||
.to_uuid()
|
||||
.ok_or(OperationError::Plugin)?;
|
||||
.ok_or(OperationError::InvalidAttribute("uuid".to_string()))?;
|
||||
audit_log!(au, "Entry valid UUID: {:?}", entry);
|
||||
match cand_uuid.insert(uuid_ref) {
|
||||
false => {
|
||||
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 => {}
|
||||
}
|
||||
|
@ -137,7 +143,9 @@ impl Plugin for Base {
|
|||
"uuid from protected system UUID range found in create set! {:?}",
|
||||
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
|
||||
);
|
||||
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
|
||||
|
@ -175,12 +185,14 @@ impl Plugin for Base {
|
|||
Ok(b) => {
|
||||
if b == true {
|
||||
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) => {
|
||||
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" {
|
||||
audit_log!(au, "Modifications to UUID's are NOT ALLOWED");
|
||||
return Err(OperationError::Plugin);
|
||||
return Err(OperationError::SystemProtectedAttribute);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -28,7 +28,7 @@ trait Plugin {
|
|||
"plugin {} has an unimplemented pre_create_transform!",
|
||||
Self::id()
|
||||
);
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn pre_create(
|
||||
|
@ -39,7 +39,7 @@ trait Plugin {
|
|||
_ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented pre_create!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn post_create(
|
||||
|
@ -50,7 +50,7 @@ trait Plugin {
|
|||
_ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented post_create!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn pre_modify(
|
||||
|
@ -60,7 +60,7 @@ trait Plugin {
|
|||
_me: &ModifyEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented pre_modify!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn post_modify(
|
||||
|
@ -72,7 +72,7 @@ trait Plugin {
|
|||
_ce: &ModifyEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented post_modify!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn pre_delete(
|
||||
|
@ -82,7 +82,7 @@ trait Plugin {
|
|||
_de: &DeleteEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented pre_delete!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn post_delete(
|
||||
|
@ -93,7 +93,7 @@ trait Plugin {
|
|||
_ce: &DeleteEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("plugin {} has an unimplemented post_delete!", Self::id());
|
||||
Err(OperationError::Plugin)
|
||||
Err(OperationError::InvalidState)
|
||||
}
|
||||
|
||||
fn verify(
|
||||
|
|
|
@ -20,7 +20,7 @@ use crate::schema::SchemaTransaction;
|
|||
use crate::server::QueryServerTransaction;
|
||||
use crate::server::{QueryServerReadTransaction, QueryServerWriteTransaction};
|
||||
use crate::value::{PartialValue, Value};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError};
|
||||
use kanidm_proto::v1::{ConsistencyError, OperationError, PluginError};
|
||||
use uuid::Uuid;
|
||||
|
||||
// NOTE: This *must* be after base.rs!!!
|
||||
|
@ -35,7 +35,14 @@ impl ReferentialIntegrity {
|
|||
uuid_value: &Value,
|
||||
) -> Result<(), OperationError> {
|
||||
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");
|
||||
// NOTE: This only checks LIVE entries (not using filter_all)
|
||||
let filt_in = filter!(f_eq("uuid", PartialValue::new_uuid(uuid.clone())));
|
||||
|
@ -53,7 +60,9 @@ impl ReferentialIntegrity {
|
|||
rtype,
|
||||
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(
|
||||
"A non-value-ref type was found.",
|
||||
"A non-value-ref type was found.".to_string(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,39 +47,47 @@ impl SchemaAttribute {
|
|||
// class
|
||||
if !value.attribute_value_pres("class", &PVCLASS_ATTRIBUTETYPE) {
|
||||
audit_log!(audit, "class attribute type not present");
|
||||
return Err(OperationError::InvalidSchemaState("missing attributetype"));
|
||||
return Err(OperationError::InvalidSchemaState(
|
||||
"missing attributetype".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// uuid
|
||||
let uuid = value.get_uuid().clone();
|
||||
|
||||
// name
|
||||
let name = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_string("attributename")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing attributename"))
|
||||
);
|
||||
let name =
|
||||
try_audit!(
|
||||
audit,
|
||||
value.get_ava_single_string("attributename").ok_or(
|
||||
OperationError::InvalidSchemaState("missing attributename".to_string())
|
||||
)
|
||||
);
|
||||
// description
|
||||
let description = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_string("description")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing description"))
|
||||
);
|
||||
let description =
|
||||
try_audit!(
|
||||
audit,
|
||||
value.get_ava_single_string("description").ok_or(
|
||||
OperationError::InvalidSchemaState("missing description".to_string())
|
||||
)
|
||||
);
|
||||
|
||||
// multivalue
|
||||
let multivalue = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_bool("multivalue")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing multivalue"))
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"missing multivalue".to_string()
|
||||
))
|
||||
);
|
||||
let unique = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_bool("unique")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing unique"))
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"missing unique".to_string()
|
||||
))
|
||||
);
|
||||
// index vec
|
||||
// even if empty, it SHOULD be present ... (is that value to put an empty set?)
|
||||
|
@ -92,7 +100,7 @@ impl SchemaAttribute {
|
|||
.into_iter()
|
||||
.map(|v: &IndexType| v.clone())
|
||||
.collect()))
|
||||
.map_err(|_| OperationError::InvalidSchemaState("Invalid index"))
|
||||
.map_err(|_| OperationError::InvalidSchemaState("Invalid index".to_string()))
|
||||
);
|
||||
// syntax type
|
||||
let syntax = try_audit!(
|
||||
|
@ -100,7 +108,9 @@ impl SchemaAttribute {
|
|||
value
|
||||
.get_ava_single_syntax("syntax")
|
||||
.and_then(|s: &SyntaxType| Some(s.clone()))
|
||||
.ok_or(OperationError::InvalidSchemaState("missing syntax"))
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"missing syntax".to_string()
|
||||
))
|
||||
);
|
||||
|
||||
Ok(SchemaAttribute {
|
||||
|
@ -339,7 +349,9 @@ impl SchemaClass {
|
|||
// Convert entry to a schema class.
|
||||
if !value.attribute_value_pres("class", &PVCLASS_CLASSTYPE) {
|
||||
audit_log!(audit, "class classtype not present");
|
||||
return Err(OperationError::InvalidSchemaState("missing classtype"));
|
||||
return Err(OperationError::InvalidSchemaState(
|
||||
"missing classtype".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// uuid
|
||||
|
@ -350,36 +362,41 @@ impl SchemaClass {
|
|||
audit,
|
||||
value
|
||||
.get_ava_single_string("classname")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing classname"))
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"missing classname".to_string()
|
||||
))
|
||||
);
|
||||
// description
|
||||
let description = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_string("description")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing description"))
|
||||
);
|
||||
let description =
|
||||
try_audit!(
|
||||
audit,
|
||||
value.get_ava_single_string("description").ok_or(
|
||||
OperationError::InvalidSchemaState("missing description".to_string())
|
||||
)
|
||||
);
|
||||
|
||||
// These are all "optional" lists of strings.
|
||||
let systemmay =
|
||||
value
|
||||
.get_ava_opt_string("systemmay")
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"Missing or invalid systemmay",
|
||||
"Missing or invalid systemmay".to_string(),
|
||||
))?;
|
||||
let systemmust =
|
||||
value
|
||||
.get_ava_opt_string("systemmust")
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"Missing or invalid systemmust",
|
||||
"Missing or invalid systemmust".to_string(),
|
||||
))?;
|
||||
let may = value
|
||||
.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
|
||||
.get_ava_opt_string("must")
|
||||
.ok_or(OperationError::InvalidSchemaState(
|
||||
"Missing or invalid must",
|
||||
"Missing or invalid must".to_string(),
|
||||
))?;
|
||||
|
||||
Ok(SchemaClass {
|
||||
|
|
|
@ -364,11 +364,11 @@ pub trait QueryServerTransaction {
|
|||
SyntaxType::UTF8STRING => Ok(Value::new_utf8(value.clone())),
|
||||
SyntaxType::UTF8STRING_INSENSITIVE => Ok(Value::new_iutf8s(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())
|
||||
.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())
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Index syntax")),
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
|
||||
SyntaxType::UUID => {
|
||||
// It's a uuid - we do NOT check for existance, because that
|
||||
// could be revealing or disclosing - it is up to acp to assert
|
||||
|
@ -386,7 +386,7 @@ pub trait QueryServerTransaction {
|
|||
Some(Value::new_uuid(un))
|
||||
})
|
||||
// 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 => {
|
||||
// See comments above.
|
||||
|
@ -398,11 +398,11 @@ pub trait QueryServerTransaction {
|
|||
Some(Value::new_refer(un))
|
||||
})
|
||||
// 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)
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Filter syntax")),
|
||||
SyntaxType::CREDENTIAL => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api")),
|
||||
.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".to_string())),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
@ -431,12 +431,15 @@ pub trait QueryServerTransaction {
|
|||
SyntaxType::UTF8STRING_INSENSITIVE => {
|
||||
Ok(PartialValue::new_iutf8s(value.as_str()))
|
||||
}
|
||||
SyntaxType::BOOLEAN => PartialValue::new_bools(value.as_str())
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid boolean syntax")),
|
||||
SyntaxType::SYNTAX_ID => PartialValue::new_syntaxs(value.as_str())
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Syntax syntax")),
|
||||
SyntaxType::INDEX_ID => PartialValue::new_indexs(value.as_str())
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Index syntax")),
|
||||
SyntaxType::BOOLEAN => PartialValue::new_bools(value.as_str()).ok_or(
|
||||
OperationError::InvalidAttribute("Invalid boolean syntax".to_string()),
|
||||
),
|
||||
SyntaxType::SYNTAX_ID => PartialValue::new_syntaxs(value.as_str()).ok_or(
|
||||
OperationError::InvalidAttribute("Invalid Syntax syntax".to_string()),
|
||||
),
|
||||
SyntaxType::INDEX_ID => PartialValue::new_indexs(value.as_str()).ok_or(
|
||||
OperationError::InvalidAttribute("Invalid Index syntax".to_string()),
|
||||
),
|
||||
SyntaxType::UUID => {
|
||||
PartialValue::new_uuids(value.as_str())
|
||||
.or_else(|| {
|
||||
|
@ -450,7 +453,9 @@ pub trait QueryServerTransaction {
|
|||
Some(PartialValue::new_uuid(un))
|
||||
})
|
||||
// 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 => {
|
||||
// See comments above.
|
||||
|
@ -462,10 +467,13 @@ pub trait QueryServerTransaction {
|
|||
Some(PartialValue::new_refer(un))
|
||||
})
|
||||
// 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)
|
||||
.ok_or(OperationError::InvalidAttribute("Invalid Filter syntax")),
|
||||
SyntaxType::JSON_FILTER => PartialValue::new_json_filter(value).ok_or(
|
||||
OperationError::InvalidAttribute("Invalid Filter syntax".to_string()),
|
||||
),
|
||||
SyntaxType::CREDENTIAL => Ok(PartialValue::new_credential_tag(value.as_str())),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue