Skip to main content

rustauth_scim/
validation.rs

1//! SCIM request validation helpers.
2
3use crate::errors::ScimError;
4use crate::mappings::{primary_email, ScimEmail};
5
6/// Returns true when `value` looks like a usable email address for provisioning.
7pub fn is_valid_email(value: &str) -> bool {
8    let value = value.trim();
9    if value.is_empty() || value.contains(char::is_whitespace) {
10        return false;
11    }
12    let Some((local, domain)) = value.split_once('@') else {
13        return false;
14    };
15    !local.is_empty()
16        && !domain.is_empty()
17        && domain.contains('.')
18        && !domain.starts_with('.')
19        && !domain.ends_with('.')
20        && !domain.contains('@')
21}
22
23/// Validates `userName` and optional `emails`, returning the canonical lowercased email.
24pub fn validate_scim_user_identity(
25    user_name: &str,
26    emails: &[ScimEmail],
27) -> Result<String, ScimError> {
28    let user_name = user_name.trim();
29    if user_name.is_empty() {
30        return Err(ScimError::bad_request("userName is required").with_scim_type("invalidValue"));
31    }
32    let email = primary_email(user_name, emails).to_ascii_lowercase();
33    if !is_valid_email(&email) {
34        return Err(ScimError::bad_request(
35            "userName and emails.value must resolve to a valid email address",
36        )
37        .with_scim_type("invalidValue"));
38    }
39    Ok(email)
40}
41
42/// Validates a SCIM `emails` array.
43pub fn validate_emails(emails: &[ScimEmail]) -> Result<(), ScimError> {
44    if emails.iter().filter(|email| email.primary).count() > 1 {
45        return Err(
46            ScimError::bad_request("Only one emails value can be primary")
47                .with_scim_type("invalidValue"),
48        );
49    }
50    for email in emails {
51        if !is_valid_email(&email.value) {
52            return Err(
53                ScimError::bad_request("emails.value must be a valid email address")
54                    .with_scim_type("invalidValue"),
55            );
56        }
57    }
58    Ok(())
59}