mirror of
https://github.com/kanidm/kanidm.git
synced 2025-04-21 09:45:39 +02:00
Progress
This commit is contained in:
parent
298ce0c9ce
commit
0fe42f62bd
|
@ -214,6 +214,7 @@ pub enum OperationError {
|
|||
SC0025UiHintSyntaxInvalid,
|
||||
SC0026Utf8SyntaxInvalid,
|
||||
SC0027ClassSetInvalid,
|
||||
SC0028CreatedUuidsInvalid,
|
||||
// Migration
|
||||
MG0001InvalidReMigrationLevel,
|
||||
MG0002RaiseDomainLevelExceedsMaximum,
|
||||
|
@ -494,6 +495,7 @@ impl OperationError {
|
|||
Self::SC0025UiHintSyntaxInvalid => Some("A SCIM UiHint contained invalid syntax".into()),
|
||||
Self::SC0026Utf8SyntaxInvalid => Some("A SCIM Utf8 String Scope Map contained invalid syntax".into()),
|
||||
Self::SC0027ClassSetInvalid => Some("The internal set of class templates used in this create operation was invalid. THIS IS A BUG.".into()),
|
||||
Self::SC0028CreatedUuidsInvalid => Some("The internal create query did not return the set of created UUIDs. THIS IS A BUG".into()),
|
||||
|
||||
Self::UI0001ChallengeSerialisation => Some("The WebAuthn challenge was unable to be serialised.".into()),
|
||||
Self::UI0002InvalidState => Some("The credential update process returned an invalid state transition.".into()),
|
||||
|
|
|
@ -32,6 +32,18 @@ pub struct ScimReference {
|
|||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
impl<T> From<T> for ScimReference
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
ScimReference {
|
||||
uuid: None,
|
||||
value: Some(value.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ScimReferences = Vec<ScimReference>;
|
||||
|
||||
#[serde_as]
|
||||
|
@ -82,21 +94,27 @@ pub struct ScimOAuth2ScopeMap {
|
|||
|
||||
#[serde_as]
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ScimEntryApplicationPost {
|
||||
pub name: String,
|
||||
pub display_name: String,
|
||||
pub displayname: String,
|
||||
pub linked_group: ScimReference,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ScimEntryApplication {
|
||||
#[serde(flatten)]
|
||||
pub header: ScimEntryHeader,
|
||||
|
||||
pub name: String,
|
||||
pub display_name: String,
|
||||
pub displayname: String,
|
||||
|
||||
pub linked_group: Vec<super::ScimReference>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub attrs: BTreeMap<Attribute, JsonValue>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
|
||||
use crate::attribute::Attribute;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::formats::CommaSeparator;
|
||||
use serde_with::{serde_as, skip_serializing_none, StringWithSeparator};
|
||||
use sshkey_attest::proto::PublicKey as SshPublicKey;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Not;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use serde_with::formats::CommaSeparator;
|
||||
use serde_with::{serde_as, skip_serializing_none, StringWithSeparator};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use self::synch::*;
|
||||
pub use scim_proto::prelude::*;
|
||||
|
@ -86,6 +86,13 @@ pub struct ScimSshPublicKey {
|
|||
pub value: SshPublicKey,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ScimReference {
|
||||
pub uuid: Uuid,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone, ToSchema)]
|
||||
pub enum ScimOauth2ClaimMapJoinChar {
|
||||
#[serde(rename = ",", alias = "csv")]
|
||||
|
|
|
@ -202,7 +202,8 @@ impl QueryServerWriteV1 {
|
|||
e
|
||||
})?;
|
||||
|
||||
let scim_create_event = ScimCreateEvent::try_from(ident, classes, entry, &mut idms_prox_write.qs_write)?;
|
||||
let scim_create_event =
|
||||
ScimCreateEvent::try_from(ident, classes, entry, &mut idms_prox_write.qs_write)?;
|
||||
|
||||
idms_prox_write
|
||||
.qs_write
|
||||
|
|
|
@ -78,6 +78,8 @@ impl Modify for SecurityAddon {
|
|||
super::v1_scim::scim_sync_get,
|
||||
super::v1_scim::scim_entry_id_get,
|
||||
super::v1_scim::scim_person_id_get,
|
||||
super::v1_scim::scim_application_post,
|
||||
super::v1_scim::scim_application_id_delete,
|
||||
|
||||
super::v1::schema_get,
|
||||
super::v1::whoami,
|
||||
|
|
|
@ -43,7 +43,7 @@ fn figure_out_if_we_have_all_the_routes() {
|
|||
.unwrap();
|
||||
// work our way through the source files in this package looking for routedefs
|
||||
let mut found_routes: BTreeMap<String, Vec<(String, String)>> = BTreeMap::new();
|
||||
let walker = walkdir::WalkDir::new(format!("{}/src", env!("CARGO_MANIFEST_DIR")))
|
||||
let walker = walkdir::WalkDir::new(format!("{}/src/https", env!("CARGO_MANIFEST_DIR")))
|
||||
.follow_links(false)
|
||||
.into_iter();
|
||||
|
||||
|
|
|
@ -393,7 +393,7 @@ async fn scim_person_id_get(
|
|||
),
|
||||
security(("token_jwt" = [])),
|
||||
tag = "scim",
|
||||
operation_id = ""
|
||||
operation_id = "scim_application_post"
|
||||
)]
|
||||
async fn scim_application_post(
|
||||
State(state): State<ServerState>,
|
||||
|
@ -427,18 +427,19 @@ async fn scim_application_post(
|
|||
),
|
||||
security(("token_jwt" = [])),
|
||||
tag = "scim",
|
||||
operation_id = "scim_person_id_get"
|
||||
operation_id = "scim_application_id_delete"
|
||||
)]
|
||||
async fn scim_application_id_delete(
|
||||
State(state): State<ServerState>,
|
||||
Path(id): Path<String>,
|
||||
Extension(kopid): Extension<KOpId>,
|
||||
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
|
||||
) -> Result<(), WebError> {
|
||||
) -> Result<Json<()>, WebError> {
|
||||
state
|
||||
.qe_w_ref
|
||||
.scim_entry_id_delete(client_auth_info, kopid.eventid, id, EntryClass::Application)
|
||||
.await
|
||||
.map(Json::from)
|
||||
.map_err(WebError::from)
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,8 @@ pub struct CreateEvent {
|
|||
pub entries: Vec<Entry<EntryInit, EntryNew>>,
|
||||
// Is the CreateEvent from an internal or external source?
|
||||
// This may affect which plugins are run ...
|
||||
/// If true, the list of created entry UUID's will be returned.
|
||||
pub return_created_uuids: bool,
|
||||
}
|
||||
|
||||
impl CreateEvent {
|
||||
|
@ -363,7 +365,11 @@ impl CreateEvent {
|
|||
// What is the correct consuming iterator here? Can we
|
||||
// even do that?
|
||||
match rentries {
|
||||
Ok(entries) => Ok(CreateEvent { ident, entries }),
|
||||
Ok(entries) => Ok(CreateEvent {
|
||||
ident,
|
||||
entries,
|
||||
return_created_uuids: false,
|
||||
}),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
@ -373,13 +379,18 @@ impl CreateEvent {
|
|||
ident: Identity,
|
||||
entries: Vec<Entry<EntryInit, EntryNew>>,
|
||||
) -> Self {
|
||||
CreateEvent { ident, entries }
|
||||
CreateEvent {
|
||||
ident,
|
||||
entries,
|
||||
return_created_uuids: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(entries: Vec<Entry<EntryInit, EntryNew>>) -> Self {
|
||||
CreateEvent {
|
||||
ident: Identity::from_internal(),
|
||||
entries,
|
||||
return_created_uuids: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -365,7 +365,7 @@ mod tests {
|
|||
let create = vec![e];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -468,7 +468,7 @@ mod tests {
|
|||
let create = vec![e];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -205,7 +205,7 @@ mod tests {
|
|||
|
||||
let create = vec![e];
|
||||
|
||||
run_create_test!(Ok(()), preload, create, None, |_| {});
|
||||
run_create_test!(Ok(None), preload, create, None, |_| {});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -464,7 +464,7 @@ mod tests {
|
|||
let create = vec![e_dyn];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -513,7 +513,7 @@ mod tests {
|
|||
let create = vec![e_group];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -562,7 +562,7 @@ mod tests {
|
|||
let create = vec![e_group];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -607,7 +607,7 @@ mod tests {
|
|||
let create = vec![e_dyn, e_group];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -108,7 +108,7 @@ mod tests {
|
|||
|
||||
let create = vec![ea];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -136,7 +136,7 @@ mod tests {
|
|||
let create = vec![e];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -858,7 +858,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
let create = vec![ea, eb];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -889,7 +889,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
let create = vec![ea, eb, ec];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -941,7 +941,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
let create = vec![ea, eb, ec];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -999,7 +999,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
let create = vec![ea, eb, ec, ed];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -181,7 +181,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
let create = vec![ea];
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -501,7 +501,7 @@ mod tests {
|
|||
let create = vec![eb];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -534,7 +534,7 @@ mod tests {
|
|||
let create = vec![e_group];
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -233,7 +233,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
@ -286,7 +286,7 @@ mod tests {
|
|||
let preload = Vec::with_capacity(0);
|
||||
|
||||
run_create_test!(
|
||||
Ok(()),
|
||||
Ok(None),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|
|
|
@ -7,7 +7,7 @@ impl QueryServerWriteTransaction<'_> {
|
|||
/// The create event is a raw, read only representation of the request
|
||||
/// that was made to us, including information about the identity
|
||||
/// performing the request.
|
||||
pub fn create(&mut self, ce: &CreateEvent) -> Result<(), OperationError> {
|
||||
pub fn create(&mut self, ce: &CreateEvent) -> Result<Option<Vec<Uuid>>, OperationError> {
|
||||
if !ce.ident.is_internal() {
|
||||
security_info!(name = %ce.ident, "create initiator");
|
||||
}
|
||||
|
@ -174,7 +174,12 @@ impl QueryServerWriteTransaction<'_> {
|
|||
} else {
|
||||
admin_info!("Create operation success");
|
||||
}
|
||||
Ok(())
|
||||
|
||||
if ce.return_created_uuids {
|
||||
Ok(Some(commit_cand.iter().map(|e| e.get_uuid()).collect()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn internal_create(
|
||||
|
@ -182,7 +187,7 @@ impl QueryServerWriteTransaction<'_> {
|
|||
entries: Vec<Entry<EntryInit, EntryNew>>,
|
||||
) -> Result<(), OperationError> {
|
||||
let ce = CreateEvent::new_internal(entries);
|
||||
self.create(&ce)
|
||||
self.create(&ce).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,12 +74,7 @@ impl ScimCreateEvent {
|
|||
})
|
||||
.collect::<Result<EntryInitNew, _>>()?;
|
||||
|
||||
|
||||
let classes =
|
||||
ValueSetIutf8::from_iter(
|
||||
classes.iter()
|
||||
.map(|cls| cls.as_ref())
|
||||
)
|
||||
let classes = ValueSetIutf8::from_iter(classes.iter().map(|cls| cls.as_ref()))
|
||||
.ok_or(OperationError::SC0027ClassSetInvalid)?;
|
||||
|
||||
entry.set_ava_set(&Attribute::Class, classes);
|
||||
|
@ -179,17 +174,21 @@ impl QueryServerWriteTransaction<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scim_create(&mut self, scim_create: ScimCreateEvent) -> Result<(), OperationError> {
|
||||
let ScimCreateEvent {
|
||||
ident, entry
|
||||
} = scim_create;
|
||||
pub fn scim_create(
|
||||
&mut self,
|
||||
scim_create: ScimCreateEvent,
|
||||
) -> Result<ScimEntryKanidm, OperationError> {
|
||||
let ScimCreateEvent { ident, entry } = scim_create;
|
||||
|
||||
let create_event = CreateEvent {
|
||||
ident,
|
||||
entries: vec![entry],
|
||||
return_created_uuids: true,
|
||||
};
|
||||
|
||||
let mut changed_uuids = self.create(&create_event)?;
|
||||
let changed_uuids = self.create(&create_event)?;
|
||||
|
||||
let mut changed_uuids = changed_uuids.ok_or(OperationError::SC0028CreatedUuidsInvalid)?;
|
||||
|
||||
let target = if let Some(target) = changed_uuids.pop() {
|
||||
if !changed_uuids.is_empty() {
|
||||
|
@ -214,7 +213,7 @@ impl QueryServerWriteTransaction<'_> {
|
|||
let f_valid = f_intent_valid.clone().into_ignore_hidden();
|
||||
|
||||
let se = SearchEvent {
|
||||
ident,
|
||||
ident: create_event.ident,
|
||||
filter: f_valid,
|
||||
filter_orig: f_intent_valid,
|
||||
// Return all attributes
|
||||
|
|
|
@ -238,6 +238,13 @@ impl ValueSetScimPut for ValueSetRefer {
|
|||
fn from_scim_json_put(value: JsonValue) -> Result<ValueSetResolveStatus, OperationError> {
|
||||
use kanidm_proto::scim_v1::client::{ScimReference, ScimReferences};
|
||||
|
||||
// May be a single reference, lets wrap it in an array to proceed.
|
||||
let value = if !value.is_array() && value.is_object() {
|
||||
JsonValue::Array(vec![value])
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
let scim_refs: ScimReferences = serde_json::from_value(value).map_err(|err| {
|
||||
warn!(?err, "Invalid SCIM reference set syntax");
|
||||
OperationError::SC0002ReferenceSyntaxInvalid
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use kanidm_proto::scim_v1::client::{ScimEntryApplicationPost, ScimReference};
|
||||
use kanidmd_testkit::{AsyncTestEnvironment, IDM_ADMIN_TEST_PASSWORD, IDM_ADMIN_TEST_USER};
|
||||
use ldap3_client::LdapClientBuilder;
|
||||
|
||||
use kanidm_proto::scim_v1::client::ScimEntryApplicationPost;
|
||||
use tracing::debug;
|
||||
|
||||
const TEST_PERSON: &str = "user_mcuserton";
|
||||
const TEST_GROUP: &str = "group_mcgroupington";
|
||||
|
||||
#[kanidmd_testkit::test(ldap = true)]
|
||||
async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
|
||||
|
@ -41,18 +42,26 @@ async fn test_ldap_application_password_basic(test_env: &AsyncTestEnvironment) {
|
|||
.await
|
||||
.expect("Failed to create the user");
|
||||
|
||||
idm_admin_rsclient
|
||||
.idm_group_create(TEST_GROUP, None)
|
||||
.await
|
||||
.expect("Failed to create test group");
|
||||
|
||||
// Create two applications
|
||||
|
||||
let application_1 = ScimEntryApplicationPost {
|
||||
name: APPLICATION_1_NAME.to_string(),
|
||||
display_name: APPLICATION_1_NAME.to_string(),
|
||||
displayname: APPLICATION_1_NAME.to_string(),
|
||||
linked_group: ScimReference::from(TEST_GROUP),
|
||||
};
|
||||
|
||||
let _application_entry = idm_admin_rsclient
|
||||
let application_entry = idm_admin_rsclient
|
||||
.idm_application_create(&application_1)
|
||||
.await
|
||||
.expect("Failed to create the user");
|
||||
|
||||
debug!(?application_entry);
|
||||
|
||||
// List, get them.
|
||||
|
||||
// Login as the person
|
||||
|
@ -69,11 +78,13 @@ async fn test_ldap_application_password_basic(test_env: &AsyncTestEnvironment) {
|
|||
|
||||
// let mut ldap_client = LdapClientBuilder::new(ldap_url).build().await.unwrap();
|
||||
|
||||
idm_admin_rsclient
|
||||
let result = idm_admin_rsclient
|
||||
.idm_application_delete(APPLICATION_1_NAME)
|
||||
.await
|
||||
.expect("Failed to create the user");
|
||||
|
||||
debug!(?result);
|
||||
|
||||
// Delete the applications
|
||||
|
||||
// Check that you can no longer bind.
|
||||
|
|
Loading…
Reference in a new issue