-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add localpart to CSV import and use it as Zitadel user ID and l…
…ocalpart metadata
- Loading branch information
Showing
8 changed files
with
203 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,6 +123,7 @@ mod tests { | |
true, | ||
None, | ||
external_user_id.to_owned(), | ||
None, | ||
) | ||
} | ||
|
||
|
@@ -296,4 +297,28 @@ mod tests { | |
// 'i'=0x69, 'd'=0x64 => "706c61696e5f6964" | ||
run_conversion_test(original_id, ExternalIdEncoding::Plain, "706c61696e5f6964"); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_localpart_preservation() { | ||
// Test that migration preserves localpart values | ||
let original_user = SyncUser::new( | ||
"first name".to_owned(), | ||
"last name".to_owned(), | ||
"[email protected]".to_owned(), | ||
None, | ||
true, | ||
None, | ||
"Y2FmZQ==".to_owned(), // base64 encoded external ID | ||
Some("test.localpart".to_owned()), // localpart should be preserved | ||
); | ||
|
||
let migrated_user = original_user | ||
.create_user_with_converted_external_id(ExternalIdEncoding::Base64) | ||
.expect("Should successfully convert user"); | ||
|
||
// External ID should be converted from base64 to hex | ||
assert_eq!(migrated_user.get_external_id(), hex::encode("cafe")); | ||
// Localpart should remain unchanged | ||
assert_eq!(migrated_user.get_localpart(), Some("test.localpart")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,9 @@ struct CsvData { | |
last_name: String, | ||
/// The user's phone number | ||
phone: String, | ||
/// The user's localpart (optional) | ||
#[serde(default)] | ||
localpart: String, | ||
} | ||
|
||
impl CsvData { | ||
|
@@ -81,6 +84,7 @@ impl CsvData { | |
preferred_username: Some(csv_data.email.clone()), | ||
external_user_id: hex::encode(csv_data.email), | ||
enabled: true, | ||
localpart: (!csv_data.localpart.is_empty()).then_some(csv_data.localpart), | ||
} | ||
} | ||
} | ||
|
@@ -139,11 +143,11 @@ mod tests { | |
fn test_get_users() { | ||
let mut config = load_config(); | ||
let csv_content = indoc! {r#" | ||
email,first_name,last_name,phone | ||
[email protected],John,Doe,+1111111111 | ||
[email protected],Jane,Smith,+2222222222 | ||
[email protected],Alice,Johnson, | ||
[email protected],Bob,Williams,+4444444444 | ||
email,first_name,last_name,phone,localpart | ||
[email protected],John,Doe,+1111111111,john.doe | ||
[email protected],Jane,Smith,+2222222222, | ||
[email protected],Alice,Johnson,,alice.johnson | ||
[email protected],Bob,Williams,+4444444444, | ||
"#}; | ||
let _file = test_helpers::temp_csv_file(&mut config, csv_content); | ||
|
||
|
@@ -155,18 +159,60 @@ mod tests { | |
|
||
let users = result.expect("Failed to get users"); | ||
assert_eq!(users.len(), 4, "Unexpected number of users"); | ||
|
||
// Test user with localpart | ||
assert_eq!(users[0].first_name, "John", "Unexpected first name at index 0"); | ||
assert_eq!(users[0].email, "[email protected]", "Unexpected email at index 0"); | ||
assert_eq!(users[3].last_name, "Williams", "Unexpected last name at index 3"); | ||
assert_eq!( | ||
users[0].external_user_id, | ||
hex::encode("[email protected]".as_bytes()), | ||
"Unexpected external_user_id at index 0" | ||
); | ||
assert_eq!( | ||
users[0].localpart, | ||
Some("john.doe".to_owned()), | ||
"Unexpected localpart at index 0" | ||
); | ||
|
||
// Test user without localpart (empty string) | ||
assert_eq!(users[1].email, "[email protected]", "Unexpected email at index 1"); | ||
assert_eq!( | ||
users[1].external_user_id, | ||
hex::encode("[email protected]".as_bytes()), | ||
"Unexpected external_user_id at index 1" | ||
); | ||
assert_eq!(users[1].localpart, None, "Unexpected localpart at index 1"); | ||
|
||
// Test user with localpart but no phone | ||
assert_eq!(users[2].email, "[email protected]", "Unexpected email at index 2"); | ||
assert_eq!( | ||
users[2].external_user_id, | ||
hex::encode("[email protected]".as_bytes()), | ||
"Unexpected external_user_id at index 2" | ||
); | ||
assert_eq!( | ||
users[2].localpart, | ||
Some("alice.johnson".to_owned()), | ||
"Unexpected localpart at index 2" | ||
); | ||
assert_eq!(users[2].phone, None, "Unexpected phone at index 2"); | ||
|
||
// Test user without localpart (empty string) but with phone | ||
assert_eq!(users[3].email, "[email protected]", "Unexpected email at index 3"); | ||
assert_eq!( | ||
users[3].external_user_id, | ||
hex::encode("[email protected]".as_bytes()), | ||
"Unexpected external_user_id at index 3" | ||
); | ||
assert_eq!(users[3].localpart, None, "Unexpected localpart at index 3"); | ||
assert_eq!(users[3].phone, Some("+4444444444".to_owned()), "Unexpected phone at index 3"); | ||
} | ||
|
||
#[test] | ||
fn test_get_users_empty_file() { | ||
let mut config = load_config(); | ||
let csv_content = indoc! {r#" | ||
email,first_name,last_name,phone | ||
email,first_name,last_name,phone,localpart | ||
"#}; | ||
let _file = test_helpers::temp_csv_file(&mut config, csv_content); | ||
|
||
|
@@ -204,7 +250,7 @@ mod tests { | |
let mut config = load_config(); | ||
let csv_content = indoc! {r#" | ||
first_name | ||
[email protected],John,Doe,+1111111111 | ||
[email protected],John,Doe,+1111111111,john.doe | ||
"#}; | ||
let _file = test_helpers::temp_csv_file(&mut config, csv_content); | ||
|
||
|
@@ -220,9 +266,9 @@ mod tests { | |
fn test_get_users_invalid_content() { | ||
let mut config = load_config(); | ||
let csv_content = indoc! {r#" | ||
email,first_name,last_name,phone | ||
email,first_name,last_name,phone,localpart | ||
[email protected] | ||
[email protected],Jane,Smith,+2222222222 | ||
[email protected],Jane,Smith,+2222222222,jane.smith | ||
"#}; | ||
let _file = test_helpers::temp_csv_file(&mut config, csv_content); | ||
|
||
|
@@ -236,5 +282,41 @@ mod tests { | |
assert_eq!(users.len(), 1, "Unexpected number of users"); | ||
assert_eq!(users[0].email, "[email protected]", "Unexpected email at index 0"); | ||
assert_eq!(users[0].last_name, "Smith", "Unexpected last name at index 0"); | ||
assert_eq!( | ||
users[0].external_user_id, | ||
hex::encode("[email protected]".as_bytes()), | ||
"Unexpected external_user_id at index 0" | ||
); | ||
assert_eq!( | ||
users[0].localpart, | ||
Some("jane.smith".to_owned()), | ||
"Unexpected localpart at index 0" | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_backward_compatibility() { | ||
// Test that old CSV format without localpart column still works | ||
let mut config = load_config(); | ||
let csv_content = indoc! {r#" | ||
email,first_name,last_name,phone | ||
[email protected],John,Doe,+1111111111 | ||
[email protected],Jane,Smith,+2222222222 | ||
"#}; | ||
let _file = test_helpers::temp_csv_file(&mut config, csv_content); | ||
|
||
let csv_config = config.sources.csv.expect("CsvSource configuration is missing"); | ||
let csv = CsvSource::new(csv_config); | ||
|
||
let result = csv.read_csv(); | ||
assert!(result.is_ok(), "Failed to get users: {:?}", result); | ||
|
||
let users = result.expect("Failed to get users"); | ||
assert_eq!(users.len(), 2, "Unexpected number of users"); | ||
// All users should have None localpart | ||
assert!( | ||
users.iter().all(|u| u.localpart.is_none()), | ||
"Expected all users to have None localpart" | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -128,6 +128,7 @@ impl LdapSource { | |
external_user_id: ldap_user_id, | ||
phone, | ||
enabled, | ||
localpart: None, | ||
}) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.