Resolve incorrect SCIM Sync serialisation (#3047)

This commit is contained in:
Firstyear 2024-09-17 16:27:41 +10:00 committed by GitHub
parent 004e263f90
commit fb3e7a01bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 4 deletions

1
Cargo.lock generated
View file

@ -5375,6 +5375,7 @@ dependencies = [
"peg",
"serde",
"serde_json",
"serde_with",
"time",
"tracing",
"tracing-subscriber",

View file

@ -19,6 +19,7 @@ doctest = false
base64urlsafedata = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
serde_with = { workspace = true }
peg = { workspace = true }
time = { workspace = true, features = [
"local-offset",

View file

@ -5,7 +5,9 @@ use url::Url;
use uuid::Uuid;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Name {
@ -73,7 +75,8 @@ impl fmt::Display for Timezone {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct MultiValueAttr {
#[serde(rename = "type")]
@ -85,6 +88,7 @@ pub struct MultiValueAttr {
pub value: String,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Photo {
@ -97,6 +101,7 @@ pub struct Photo {
value: Url,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Binary {
#[serde(rename = "type")]
@ -108,6 +113,7 @@ pub struct Binary {
value: Base64UrlSafeData,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Address {
@ -130,6 +136,7 @@ enum Membership {
}
*/
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Group {
@ -141,6 +148,7 @@ pub struct Group {
display: String,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct User {
@ -163,17 +171,23 @@ pub struct User {
timezone: Option<Timezone>,
active: bool,
password: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
emails: Vec<MultiValueAttr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
phone_numbers: Vec<MultiValueAttr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
ims: Vec<MultiValueAttr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
photos: Vec<Photo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
addresses: Vec<Address>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
groups: Vec<Group>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
entitlements: Vec<MultiValueAttr>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
roles: Vec<MultiValueAttr>,
#[serde(default)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
x509certificates: Vec<Binary>,
}

View file

@ -59,4 +59,59 @@ mod tests {
fn test_scim_kani_to_rfc() {
// Assert that a kanidm strong entry can convert to rfc.
}
#[test]
fn test_scim_sync_kani_to_rfc() {
use super::*;
// Group
let group_uuid = uuid::uuid!("2d0a9e7c-cc08-4ca2-8d7f-114f9abcfc8a");
let group = ScimSyncGroup::builder("testgroup".to_string(), group_uuid)
.set_description(Some("test desc".to_string()))
.set_gidnumber(Some(12345))
.set_members(vec!["member_a".to_string(), "member_a".to_string()].into_iter())
.set_external_id(Some("cn=testgroup".to_string()))
.build();
let entry: Result<ScimEntry, _> = group.try_into();
assert!(entry.is_ok());
// User
let user_uuid = uuid::uuid!("cb3de098-33fd-4565-9d80-4f7ed6a664e9");
let user_sshkey = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBENubZikrb8hu+HeVRdZ0pp/VAk2qv4JDbuJhvD0yNdWDL2e3cBbERiDeNPkWx58Q4rVnxkbV1fa8E2waRtT91wAAAAEc3NoOg== testuser@fidokey";
let person =
ScimSyncPerson::builder(user_uuid, "testuser".to_string(), "Test User".to_string())
.set_password_import(Some("new_password".to_string()))
.set_unix_password_import(Some("new_password".to_string()))
.set_totp_import(vec![ScimTotp {
external_id: "Totp".to_string(),
secret: "abcd".to_string(),
algo: "SHA3".to_string(),
step: 60,
digits: 8,
}])
.set_mail(vec![MultiValueAttr {
primary: Some(true),
value: "testuser@example.com".to_string(),
..Default::default()
}])
.set_ssh_publickey(vec![ScimSshPubKey {
label: "Key McKeyface".to_string(),
value: user_sshkey.to_string(),
}])
.set_login_shell(Some("/bin/false".to_string()))
.set_account_valid_from(Some("2023-11-28T04:57:55Z".to_string()))
.set_account_expire(Some("2023-11-28T04:57:55Z".to_string()))
.set_gidnumber(Some(54321))
.set_external_id(Some("cn=testuser".to_string()))
.build();
let entry: Result<ScimEntry, _> = person.try_into();
assert!(entry.is_ok());
}
}

View file

@ -5,6 +5,7 @@ use uuid::Uuid;
use scim_proto::user::MultiValueAttr;
use scim_proto::{ScimEntry, ScimEntryHeader};
use serde_with::skip_serializing_none;
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, ToSchema)]
@ -82,6 +83,7 @@ pub struct ScimSshPubKey {
pub value: String,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ScimSyncPerson {
@ -93,9 +95,12 @@ pub struct ScimSyncPerson {
pub gidnumber: Option<u32>,
pub password_import: Option<String>,
pub unix_password_import: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub totp_import: Vec<ScimTotp>,
pub login_shell: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mail: Vec<MultiValueAttr>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ssh_publickey: Vec<ScimSshPubKey>,
pub account_valid_from: Option<String>,
pub account_expire: Option<String>,