junobuild-auth 0.4.1

Authentication toolkit for Juno.
Documentation
use crate::profile::constants::{
    EMAIL_MAX_LENGTH, LOCALE_MAX_LENGTH, NAME_MAX_LENGTH, SHORT_NAME_MAX_LENGTH,
};
use crate::profile::errors::{
    JUNO_AUTH_ERROR_PROFILE_EMAIL_INVALID_LENGTH,
    JUNO_AUTH_ERROR_PROFILE_FAMILY_NAME_INVALID_LENGTH,
    JUNO_AUTH_ERROR_PROFILE_GIVEN_NAME_INVALID_LENGTH,
    JUNO_AUTH_ERROR_PROFILE_LOCALE_INVALID_LENGTH, JUNO_AUTH_ERROR_PROFILE_NAME_INVALID_LENGTH,
    JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_SCHEME, JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_URL,
    JUNO_AUTH_ERROR_PROFILE_PREFERRED_USERNAME_INVALID_LENGTH,
};
use crate::profile::types::{OpenIdProfile, Validated};
use url::Url;

impl<T: OpenIdProfile> Validated for T {
    fn validate(&self) -> Result<(), String> {
        if let Some(email) = self.email().as_ref() {
            if email.len() > EMAIL_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_EMAIL_INVALID_LENGTH.to_string());
            }
        }

        if let Some(name) = self.name().as_ref() {
            if name.chars().count() > NAME_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_NAME_INVALID_LENGTH.to_string());
            }
        }

        if let Some(given_name) = self.given_name().as_ref() {
            if given_name.chars().count() > SHORT_NAME_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_GIVEN_NAME_INVALID_LENGTH.to_string());
            }
        }

        if let Some(family_name) = self.family_name().as_ref() {
            if family_name.chars().count() > SHORT_NAME_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_FAMILY_NAME_INVALID_LENGTH.to_string());
            }
        }

        if let Some(preferred_username) = self.preferred_username().as_ref() {
            if preferred_username.chars().count() > SHORT_NAME_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_PREFERRED_USERNAME_INVALID_LENGTH.to_string());
            }
        }

        if let Some(locale) = self.locale().as_ref() {
            if locale.chars().count() > LOCALE_MAX_LENGTH {
                return Err(JUNO_AUTH_ERROR_PROFILE_LOCALE_INVALID_LENGTH.to_string());
            }
        }

        if let Some(picture) = self.picture().as_ref() {
            let url = Url::parse(picture)
                .map_err(|_| JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_URL.to_string())?;

            if url.scheme() != "https" {
                return Err(JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_SCHEME.to_string());
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    pub struct OpenIdDataTest {
        pub email: Option<String>,
        pub name: Option<String>,
        pub given_name: Option<String>,
        pub family_name: Option<String>,
        pub preferred_username: Option<String>,
        pub picture: Option<String>,
        pub locale: Option<String>,
    }

    impl OpenIdProfile for OpenIdDataTest {
        fn email(&self) -> Option<&str> {
            self.email.as_deref()
        }
        fn name(&self) -> Option<&str> {
            self.name.as_deref()
        }
        fn given_name(&self) -> Option<&str> {
            self.given_name.as_deref()
        }
        fn family_name(&self) -> Option<&str> {
            self.family_name.as_deref()
        }
        fn preferred_username(&self) -> Option<&str> {
            self.preferred_username.as_deref()
        }
        fn picture(&self) -> Option<&str> {
            self.picture.as_deref()
        }
        fn locale(&self) -> Option<&str> {
            self.locale.as_deref()
        }
    }

    #[test]
    fn test_google_valid_data() {
        let data = OpenIdDataTest {
            email: Some("user@example.com".to_string()),
            name: Some("Ada Lovelace".to_string()),
            given_name: Some("Ada".to_string()),
            family_name: Some("Lovelace".to_string()),
            preferred_username: None,
            picture: Some("https://example.com/avatar.png".to_string()),
            locale: Some("en".to_string()),
        };

        assert!(data.validate().is_ok());
    }

    #[test]
    fn test_google_invalid_email_length() {
        let long_email = "a".repeat(EMAIL_MAX_LENGTH + 1);
        let data = OpenIdDataTest {
            email: Some(long_email),
            name: None,
            given_name: None,
            family_name: None,
            preferred_username: None,
            picture: None,
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_picture_url() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: None,
            family_name: None,
            preferred_username: None,
            picture: Some("not-a-valid-url".to_string()),
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_picture_scheme() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: None,
            family_name: None,
            preferred_username: None,
            picture: Some("http://example.com/avatar.png".to_string()),
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_name_length() {
        let data = OpenIdDataTest {
            email: None,
            name: Some("a".repeat(NAME_MAX_LENGTH + 1)),
            given_name: None,
            family_name: None,
            preferred_username: None,
            picture: None,
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_given_name_length() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
            family_name: None,
            preferred_username: None,
            picture: None,
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_family_name_length() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: None,
            family_name: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
            preferred_username: None,
            picture: None,
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_google_invalid_locale_length() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: None,
            family_name: None,
            preferred_username: None,
            picture: None,
            locale: Some("a".repeat(LOCALE_MAX_LENGTH + 1)),
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_github_invalid_preferred_username_length() {
        let data = OpenIdDataTest {
            email: None,
            name: None,
            given_name: None,
            family_name: None,
            preferred_username: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
            picture: None,
            locale: None,
        };
        assert!(data.validate().is_err());
    }

    #[test]
    fn test_github_valid_data() {
        let data = OpenIdDataTest {
            email: Some("user@example.com".to_string()),
            name: Some("Peter Peter Parker".to_string()),
            given_name: None,
            family_name: None,
            preferred_username: Some("peterpeterparker".to_string()),
            picture: Some("https://avatars.githubusercontent.com/u/16886711".to_string()),
            locale: None,
        };

        assert!(data.validate().is_ok());
    }
}