mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
1737 1739 sync - map uidnumbers mail (#1741)
This commit is contained in:
parent
c65be8174a
commit
6513fae5e2
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -95,9 +95,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4f263788a35611fba42eb41ff811c5d0360c58b97402570312a350736e2542e"
|
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
|
@ -477,7 +477,7 @@ dependencies = [
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_cbor",
|
"serde_cbor",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.6",
|
"sha2 0.10.7",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1006,9 +1006,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.7"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -3072,9 +3072,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "6.0.0"
|
version = "6.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d9ba6c734de18ca27c8cef5cd7058aa4ac9f63596131e4c7e41e579319032a2"
|
checksum = "5738a2795d57ea20abec2d6d76c6081186709c0024187cd5977265eda6598b51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
@ -3228,7 +3228,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
"sha2 0.10.6",
|
"sha2 0.10.7",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
@ -4079,9 +4079,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scim_proto"
|
name = "scim_proto"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3fbb159b673baace5ca93f155fdf80a957631802a04950df298c6e2445e3f4e"
|
checksum = "38e53f2c444b72dd7410aa1cdc3c0942349262e84364dc7968dc7402525ea2ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64urlsafedata",
|
"base64urlsafedata",
|
||||||
"peg",
|
"peg",
|
||||||
|
@ -4241,9 +4241,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.96"
|
version = "1.0.97"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -4324,9 +4324,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.6"
|
version = "0.10.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
|
@ -5247,11 +5247,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
|
||||||
"try-lock",
|
"try-lock",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -5717,9 +5716,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.4.6"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
|
checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
|
@ -119,8 +119,8 @@ reqwest = { version = "0.11.18", default-features = false, features=["cookies",
|
||||||
rpassword = "^7.2.0"
|
rpassword = "^7.2.0"
|
||||||
rusqlite = "^0.28.0"
|
rusqlite = "^0.28.0"
|
||||||
|
|
||||||
scim_proto = "^0.2.0"
|
scim_proto = "^0.2.1"
|
||||||
# scim_proto = { path = "../scim/proto", version = "^0.2.0" }
|
# scim_proto = { path = "../scim/proto", version = "^0.2.1" }
|
||||||
# scim_proto = { git = "https://github.com/kanidm/scim.git", version = "0.1.1" }
|
# scim_proto = { git = "https://github.com/kanidm/scim.git", version = "0.1.1" }
|
||||||
|
|
||||||
selinux = "^0.4.1"
|
selinux = "^0.4.1"
|
||||||
|
|
|
@ -36,3 +36,14 @@ ipa_sync_base_dn = "dc=ipa,dc=dev,dc=kanidm,dc=com"
|
||||||
# my-problematic-entry
|
# my-problematic-entry
|
||||||
exclude = true
|
exclude = true
|
||||||
|
|
||||||
|
# Remap the uuid of this entry to a new uuid on Kanidm
|
||||||
|
#
|
||||||
|
# map_uuid = <uuid>
|
||||||
|
|
||||||
|
# Remap the name of this entry to a new name on Kanidm
|
||||||
|
#
|
||||||
|
# map_name = <name>
|
||||||
|
|
||||||
|
# Remap the gidnumber for groups, and uidnumber for users
|
||||||
|
#
|
||||||
|
# map_gidnumber = <number>
|
||||||
|
|
|
@ -74,3 +74,17 @@ ldap_filter = "(|(objectclass=person)(objectclass=posixgroup))"
|
||||||
# my-problematic-entry
|
# my-problematic-entry
|
||||||
exclude = true
|
exclude = true
|
||||||
|
|
||||||
|
# Remap the uuid of this entry to a new uuid on Kanidm
|
||||||
|
#
|
||||||
|
# map_uuid = <uuid>
|
||||||
|
|
||||||
|
# Remap the name of this entry to a new name on Kanidm
|
||||||
|
#
|
||||||
|
# map_name = <name>
|
||||||
|
|
||||||
|
# Remap the gidnumber for groups, and uidnumber for users
|
||||||
|
#
|
||||||
|
# map_gidnumber = <number>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ impl CryptoPolicy {
|
||||||
None => PBKDF2_MIN_NIST_COST,
|
None => PBKDF2_MIN_NIST_COST,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Argon2id has multiple paramaters. These all are about *exchanges* that you can
|
// Argon2id has multiple parameters. These all are about *exchanges* that you can
|
||||||
// request in how the computation is performed.
|
// request in how the computation is performed.
|
||||||
//
|
//
|
||||||
// rfc9106 explains that there are two algorithms stacked here. Argon2i has defences
|
// rfc9106 explains that there are two algorithms stacked here. Argon2i has defences
|
||||||
|
|
|
@ -21,11 +21,12 @@ impl Default for CpuOptLevel {
|
||||||
CpuOptLevel::x86_64_v2
|
CpuOptLevel::x86_64_v2
|
||||||
} else if cfg!(target_arch = "aarch64") && cfg!(target_os = "macos") {
|
} else if cfg!(target_arch = "aarch64") && cfg!(target_os = "macos") {
|
||||||
CpuOptLevel::apple_m1
|
CpuOptLevel::apple_m1
|
||||||
|
/*
|
||||||
} else if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") {
|
} else if cfg!(target_arch = "aarch64") && cfg!(target_os = "linux") {
|
||||||
// Disable neon_v8 on linux - this has issues on non-apple hardware and on
|
// Disable neon_v8 on linux - this has issues on non-apple hardware and on
|
||||||
// opensuse/distro builds.
|
// opensuse/distro builds.
|
||||||
// CpuOptLevel::neon_v8
|
CpuOptLevel::neon_v8
|
||||||
CpuOptLevel::none
|
*/
|
||||||
} else {
|
} else {
|
||||||
CpuOptLevel::none
|
CpuOptLevel::none
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::collections::BTreeMap;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub use scim_proto::prelude::{ScimAttr, ScimComplexAttr, ScimEntry, ScimError, ScimSimpleAttr};
|
pub use scim_proto::prelude::{ScimAttr, ScimComplexAttr, ScimEntry, ScimError, ScimSimpleAttr};
|
||||||
|
pub use scim_proto::user::MultiValueAttr;
|
||||||
use scim_proto::*;
|
use scim_proto::*;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -105,6 +106,7 @@ pub struct ScimSyncPerson {
|
||||||
pub password_import: Option<String>,
|
pub password_import: Option<String>,
|
||||||
pub totp_import: Vec<ScimTotp>,
|
pub totp_import: Vec<ScimTotp>,
|
||||||
pub login_shell: Option<String>,
|
pub login_shell: Option<String>,
|
||||||
|
pub mail: Vec<MultiValueAttr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to allow this because clippy is broken and doesn't realise scimentry is out of crate
|
// Need to allow this because clippy is broken and doesn't realise scimentry is out of crate
|
||||||
|
@ -121,6 +123,7 @@ impl Into<ScimEntry> for ScimSyncPerson {
|
||||||
password_import,
|
password_import,
|
||||||
totp_import,
|
totp_import,
|
||||||
login_shell,
|
login_shell,
|
||||||
|
mail,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let schemas = if gidnumber.is_some() {
|
let schemas = if gidnumber.is_some() {
|
||||||
|
@ -144,6 +147,7 @@ impl Into<ScimEntry> for ScimSyncPerson {
|
||||||
set_option_string!(attrs, "password_import", password_import);
|
set_option_string!(attrs, "password_import", password_import);
|
||||||
set_multi_complex!(attrs, "totp_import", totp_import);
|
set_multi_complex!(attrs, "totp_import", totp_import);
|
||||||
set_option_string!(attrs, "loginshell", login_shell);
|
set_option_string!(attrs, "loginshell", login_shell);
|
||||||
|
set_multi_complex!(attrs, "mail", mail);
|
||||||
|
|
||||||
ScimEntry {
|
ScimEntry {
|
||||||
schemas,
|
schemas,
|
||||||
|
|
|
@ -1014,6 +1014,46 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
}
|
}
|
||||||
Ok(vs)
|
Ok(vs)
|
||||||
}
|
}
|
||||||
|
(SyntaxType::EmailAddress, true, ScimAttr::MultiComplex(values)) => {
|
||||||
|
let mut vs = Vec::with_capacity(values.len());
|
||||||
|
for complex in values.iter() {
|
||||||
|
let mail_addr = complex
|
||||||
|
.attrs
|
||||||
|
.get("value")
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error!("Invalid scim complex attr - missing required key value");
|
||||||
|
OperationError::InvalidAttribute(format!(
|
||||||
|
"missing required key value - {scim_attr_name}"
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.and_then(|external_id| match external_id {
|
||||||
|
ScimSimpleAttr::String(value) => Ok(value.clone()),
|
||||||
|
_ => {
|
||||||
|
error!("Invalid value attribute - must be scim simple string");
|
||||||
|
Err(OperationError::InvalidAttribute(format!(
|
||||||
|
"value must be scim simple string - {scim_attr_name}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let primary = if let Some(primary) = complex.attrs.get("primary") {
|
||||||
|
match primary {
|
||||||
|
ScimSimpleAttr::Bool(value) => Ok(*value),
|
||||||
|
_ => {
|
||||||
|
error!("Invalid primary attribute - must be scim simple bool");
|
||||||
|
Err(OperationError::InvalidAttribute(format!(
|
||||||
|
"primary must be scim simple bool - {scim_attr_name}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}?
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
vs.push(Value::EmailAddress(mail_addr, primary))
|
||||||
|
}
|
||||||
|
Ok(vs)
|
||||||
|
}
|
||||||
(syn, mv, sa) => {
|
(syn, mv, sa) => {
|
||||||
error!(?syn, ?mv, ?sa, "Unsupported scim attribute conversion. This may be a syntax error in your import, or a missing feature in Kanidm.");
|
error!(?syn, ?mv, ?sa, "Unsupported scim attribute conversion. This may be a syntax error in your import, or a missing feature in Kanidm.");
|
||||||
Err(OperationError::InvalidAttribute(format!(
|
Err(OperationError::InvalidAttribute(format!(
|
||||||
|
@ -2914,6 +2954,11 @@ mod tests {
|
||||||
"gidnumber": 12345,
|
"gidnumber": 12345,
|
||||||
"loginshell": "/bin/sh",
|
"loginshell": "/bin/sh",
|
||||||
"name": "testuser",
|
"name": "testuser",
|
||||||
|
"mail": [
|
||||||
|
{
|
||||||
|
"value": "testuser@dev.blackhats.net.au"
|
||||||
|
}
|
||||||
|
],
|
||||||
"password_import": "ipaNTHash: iEb36u6PsRetBr3YMLdYbA"
|
"password_import": "ipaNTHash: iEb36u6PsRetBr3YMLdYbA"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,7 +159,7 @@ impl ValueSetT for ValueSetCid {
|
||||||
|
|
||||||
fn to_cid_single(&self) -> Option<Cid> {
|
fn to_cid_single(&self) -> Option<Cid> {
|
||||||
if self.set.len() == 1 {
|
if self.set.len() == 1 {
|
||||||
self.set.iter().take(1).cloned().next()
|
self.set.iter().take(1).next().cloned()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,11 @@ pub struct Config {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Default, Clone)]
|
||||||
pub struct EntryConfig {
|
pub struct EntryConfig {
|
||||||
// uuid: Uuid,
|
|
||||||
|
|
||||||
// Default false
|
// Default false
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub exclude: bool,
|
pub exclude: bool,
|
||||||
// map_uuid: Option<Uuid>,
|
|
||||||
// map_external_id: Option<String>,
|
pub map_uuid: Option<Uuid>,
|
||||||
// map_name: Option<String>,
|
pub map_name: Option<String>,
|
||||||
|
pub map_gidnumber: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use kanidm_client::KanidmClientBuilder;
|
use kanidm_client::KanidmClientBuilder;
|
||||||
use kanidm_proto::scim_v1::{
|
use kanidm_proto::scim_v1::{
|
||||||
ScimEntry, ScimExternalMember, ScimSyncGroup, ScimSyncPerson, ScimSyncRequest,
|
MultiValueAttr, ScimEntry, ScimExternalMember, ScimSyncGroup, ScimSyncPerson, ScimSyncRequest,
|
||||||
ScimSyncRetentionMode, ScimSyncState, ScimTotp,
|
ScimSyncRetentionMode, ScimSyncState, ScimTotp,
|
||||||
};
|
};
|
||||||
use kanidmd_lib::utils::file_permissions_readonly;
|
use kanidmd_lib::utils::file_permissions_readonly;
|
||||||
|
@ -708,11 +708,19 @@ fn ipa_to_scim_entry(
|
||||||
mut entry,
|
mut entry,
|
||||||
} = sync_entry;
|
} = sync_entry;
|
||||||
|
|
||||||
let id = entry_uuid;
|
let id = if let Some(map_uuid) = &entry_config.map_uuid {
|
||||||
|
*map_uuid
|
||||||
|
} else {
|
||||||
|
entry_uuid
|
||||||
|
};
|
||||||
|
|
||||||
let user_name = entry.remove_ava_single("uid").ok_or_else(|| {
|
let user_name = if let Some(name) = entry_config.map_name.clone() {
|
||||||
error!("Missing required attribute uid");
|
name
|
||||||
})?;
|
} else {
|
||||||
|
entry.remove_ava_single("uid").ok_or_else(|| {
|
||||||
|
error!("Missing required attribute uid");
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
// ⚠️ hardcoded skip on admin here!!!
|
// ⚠️ hardcoded skip on admin here!!!
|
||||||
if user_name == "admin" {
|
if user_name == "admin" {
|
||||||
|
@ -724,14 +732,18 @@ fn ipa_to_scim_entry(
|
||||||
error!("Missing required attribute cn");
|
error!("Missing required attribute cn");
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let gidnumber = entry
|
let gidnumber = if let Some(number) = entry_config.map_gidnumber.clone() {
|
||||||
.remove_ava_single("gidnumber")
|
Some(number)
|
||||||
.map(|gid| {
|
} else {
|
||||||
u32::from_str(&gid).map_err(|_| {
|
entry
|
||||||
error!("Invalid gidnumber");
|
.remove_ava_single("gidnumber")
|
||||||
|
.map(|gid| {
|
||||||
|
u32::from_str(&gid).map_err(|_| {
|
||||||
|
error!("Invalid gidnumber");
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
.transpose()?
|
||||||
.transpose()?;
|
};
|
||||||
|
|
||||||
let password_import = entry
|
let password_import = entry
|
||||||
.remove_ava_single("ipanthash")
|
.remove_ava_single("ipanthash")
|
||||||
|
@ -741,6 +753,21 @@ fn ipa_to_scim_entry(
|
||||||
// pw hash formats in 389-ds we don't support!
|
// pw hash formats in 389-ds we don't support!
|
||||||
.or_else(|| entry.remove_ava_single("userpassword"));
|
.or_else(|| entry.remove_ava_single("userpassword"));
|
||||||
|
|
||||||
|
let mail: Vec<_> = entry
|
||||||
|
.remove_ava("mail")
|
||||||
|
.map(|set| {
|
||||||
|
set.into_iter()
|
||||||
|
.map(|addr| MultiValueAttr {
|
||||||
|
type_: None,
|
||||||
|
primary: None,
|
||||||
|
display: None,
|
||||||
|
ref_: None,
|
||||||
|
value: addr,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let totp_import = if !totp.is_empty() {
|
let totp_import = if !totp.is_empty() {
|
||||||
if password_import.is_some() {
|
if password_import.is_some() {
|
||||||
// If there are TOTP's, convert them to something sensible.
|
// If there are TOTP's, convert them to something sensible.
|
||||||
|
@ -769,6 +796,7 @@ fn ipa_to_scim_entry(
|
||||||
password_import,
|
password_import,
|
||||||
totp_import,
|
totp_import,
|
||||||
login_shell,
|
login_shell,
|
||||||
|
mail,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
))
|
))
|
||||||
|
|
|
@ -29,6 +29,10 @@ fn person_attr_login_shell() -> String {
|
||||||
"loginshell".to_string()
|
"loginshell".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn person_attr_mail() -> String {
|
||||||
|
"mail".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn group_objectclass() -> String {
|
fn group_objectclass() -> String {
|
||||||
"groupofnames".to_string()
|
"groupofnames".to_string()
|
||||||
}
|
}
|
||||||
|
@ -75,6 +79,8 @@ pub struct Config {
|
||||||
pub person_password_prefix: Option<String>,
|
pub person_password_prefix: Option<String>,
|
||||||
#[serde(default = "person_attr_login_shell")]
|
#[serde(default = "person_attr_login_shell")]
|
||||||
pub person_attr_login_shell: String,
|
pub person_attr_login_shell: String,
|
||||||
|
#[serde(default = "person_attr_mail")]
|
||||||
|
pub person_attr_mail: String,
|
||||||
|
|
||||||
#[serde(default = "group_objectclass")]
|
#[serde(default = "group_objectclass")]
|
||||||
pub group_objectclass: String,
|
pub group_objectclass: String,
|
||||||
|
@ -93,12 +99,11 @@ pub struct Config {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Default, Clone)]
|
||||||
pub struct EntryConfig {
|
pub struct EntryConfig {
|
||||||
// uuid: Uuid,
|
|
||||||
|
|
||||||
// Default false
|
// Default false
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub exclude: bool,
|
pub exclude: bool,
|
||||||
// map_uuid: Option<Uuid>,
|
|
||||||
// map_external_id: Option<String>,
|
pub map_uuid: Option<Uuid>,
|
||||||
// map_name: Option<String>,
|
pub map_name: Option<String>,
|
||||||
|
pub map_gidnumber: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ use tracing_subscriber::{fmt, EnvFilter};
|
||||||
|
|
||||||
use kanidm_client::KanidmClientBuilder;
|
use kanidm_client::KanidmClientBuilder;
|
||||||
use kanidm_proto::scim_v1::{
|
use kanidm_proto::scim_v1::{
|
||||||
ScimEntry, ScimExternalMember, ScimSyncGroup, ScimSyncPerson, ScimSyncRequest,
|
MultiValueAttr, ScimEntry, ScimExternalMember, ScimSyncGroup, ScimSyncPerson, ScimSyncRequest,
|
||||||
ScimSyncRetentionMode, ScimSyncState,
|
ScimSyncRetentionMode, ScimSyncState,
|
||||||
};
|
};
|
||||||
use kanidmd_lib::utils::file_permissions_readonly;
|
use kanidmd_lib::utils::file_permissions_readonly;
|
||||||
|
@ -477,16 +477,24 @@ fn ldap_to_scim_entry(
|
||||||
mut entry,
|
mut entry,
|
||||||
} = sync_entry;
|
} = sync_entry;
|
||||||
|
|
||||||
let id = entry_uuid;
|
let id = if let Some(map_uuid) = &entry_config.map_uuid {
|
||||||
|
*map_uuid
|
||||||
|
} else {
|
||||||
|
entry_uuid
|
||||||
|
};
|
||||||
|
|
||||||
let user_name = entry
|
let user_name = if let Some(name) = entry_config.map_name.clone() {
|
||||||
.remove_ava_single(&sync_config.person_attr_user_name)
|
name
|
||||||
.ok_or_else(|| {
|
} else {
|
||||||
error!(
|
entry
|
||||||
"Missing required attribute {} (person_attr_user_name)",
|
.remove_ava_single(&sync_config.person_attr_user_name)
|
||||||
sync_config.person_attr_user_name
|
.ok_or_else(|| {
|
||||||
);
|
error!(
|
||||||
})?;
|
"Missing required attribute {} (person_attr_user_name)",
|
||||||
|
sync_config.person_attr_user_name
|
||||||
|
);
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
let display_name = entry
|
let display_name = entry
|
||||||
.remove_ava_single(&sync_config.person_attr_display_name)
|
.remove_ava_single(&sync_config.person_attr_display_name)
|
||||||
|
@ -497,17 +505,21 @@ fn ldap_to_scim_entry(
|
||||||
);
|
);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let gidnumber = entry
|
let gidnumber = if let Some(number) = entry_config.map_gidnumber.clone() {
|
||||||
.remove_ava_single(&sync_config.person_attr_gidnumber)
|
Some(number)
|
||||||
.map(|gid| {
|
} else {
|
||||||
u32::from_str(&gid).map_err(|_| {
|
entry
|
||||||
error!(
|
.remove_ava_single(&sync_config.person_attr_gidnumber)
|
||||||
"Invalid gidnumber - {} is not a u32 (person_attr_gidnumber)",
|
.map(|gid| {
|
||||||
sync_config.person_attr_gidnumber
|
u32::from_str(&gid).map_err(|_| {
|
||||||
);
|
error!(
|
||||||
|
"Invalid gidnumber - {} is not a u32 (person_attr_gidnumber)",
|
||||||
|
sync_config.person_attr_gidnumber
|
||||||
|
);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
.transpose()?
|
||||||
.transpose()?;
|
};
|
||||||
|
|
||||||
let password_import = entry.remove_ava_single(&sync_config.person_attr_password);
|
let password_import = entry.remove_ava_single(&sync_config.person_attr_password);
|
||||||
|
|
||||||
|
@ -517,6 +529,21 @@ fn ldap_to_scim_entry(
|
||||||
password_import
|
password_import
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mail: Vec<_> = entry
|
||||||
|
.remove_ava(&sync_config.person_attr_mail)
|
||||||
|
.map(|set| {
|
||||||
|
set.into_iter()
|
||||||
|
.map(|addr| MultiValueAttr {
|
||||||
|
type_: None,
|
||||||
|
primary: None,
|
||||||
|
display: None,
|
||||||
|
ref_: None,
|
||||||
|
value: addr,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let totp_import = Vec::default();
|
let totp_import = Vec::default();
|
||||||
|
|
||||||
let login_shell = entry.remove_ava_single(&sync_config.person_attr_login_shell);
|
let login_shell = entry.remove_ava_single(&sync_config.person_attr_login_shell);
|
||||||
|
@ -532,6 +559,7 @@ fn ldap_to_scim_entry(
|
||||||
password_import,
|
password_import,
|
||||||
totp_import,
|
totp_import,
|
||||||
login_shell,
|
login_shell,
|
||||||
|
mail,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
))
|
))
|
||||||
|
|
Loading…
Reference in a new issue