Skip to main content

junobuild_auth/profile/
impls.rs

1use crate::profile::constants::{
2    EMAIL_MAX_LENGTH, LOCALE_MAX_LENGTH, NAME_MAX_LENGTH, SHORT_NAME_MAX_LENGTH,
3};
4use crate::profile::errors::{
5    JUNO_AUTH_ERROR_PROFILE_EMAIL_INVALID_LENGTH,
6    JUNO_AUTH_ERROR_PROFILE_FAMILY_NAME_INVALID_LENGTH,
7    JUNO_AUTH_ERROR_PROFILE_GIVEN_NAME_INVALID_LENGTH,
8    JUNO_AUTH_ERROR_PROFILE_LOCALE_INVALID_LENGTH, JUNO_AUTH_ERROR_PROFILE_NAME_INVALID_LENGTH,
9    JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_SCHEME, JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_URL,
10    JUNO_AUTH_ERROR_PROFILE_PREFERRED_USERNAME_INVALID_LENGTH,
11};
12use crate::profile::types::{OpenIdProfile, Validated};
13use url::Url;
14
15impl<T: OpenIdProfile> Validated for T {
16    fn validate(&self) -> Result<(), String> {
17        if let Some(email) = self.email().as_ref() {
18            if email.len() > EMAIL_MAX_LENGTH {
19                return Err(JUNO_AUTH_ERROR_PROFILE_EMAIL_INVALID_LENGTH.to_string());
20            }
21        }
22
23        if let Some(name) = self.name().as_ref() {
24            if name.chars().count() > NAME_MAX_LENGTH {
25                return Err(JUNO_AUTH_ERROR_PROFILE_NAME_INVALID_LENGTH.to_string());
26            }
27        }
28
29        if let Some(given_name) = self.given_name().as_ref() {
30            if given_name.chars().count() > SHORT_NAME_MAX_LENGTH {
31                return Err(JUNO_AUTH_ERROR_PROFILE_GIVEN_NAME_INVALID_LENGTH.to_string());
32            }
33        }
34
35        if let Some(family_name) = self.family_name().as_ref() {
36            if family_name.chars().count() > SHORT_NAME_MAX_LENGTH {
37                return Err(JUNO_AUTH_ERROR_PROFILE_FAMILY_NAME_INVALID_LENGTH.to_string());
38            }
39        }
40
41        if let Some(preferred_username) = self.preferred_username().as_ref() {
42            if preferred_username.chars().count() > SHORT_NAME_MAX_LENGTH {
43                return Err(JUNO_AUTH_ERROR_PROFILE_PREFERRED_USERNAME_INVALID_LENGTH.to_string());
44            }
45        }
46
47        if let Some(locale) = self.locale().as_ref() {
48            if locale.chars().count() > LOCALE_MAX_LENGTH {
49                return Err(JUNO_AUTH_ERROR_PROFILE_LOCALE_INVALID_LENGTH.to_string());
50            }
51        }
52
53        if let Some(picture) = self.picture().as_ref() {
54            let url = Url::parse(picture)
55                .map_err(|_| JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_URL.to_string())?;
56
57            if url.scheme() != "https" {
58                return Err(JUNO_AUTH_ERROR_PROFILE_PICTURE_INVALID_SCHEME.to_string());
59            }
60        }
61
62        Ok(())
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    pub struct OpenIdDataTest {
71        pub email: Option<String>,
72        pub name: Option<String>,
73        pub given_name: Option<String>,
74        pub family_name: Option<String>,
75        pub preferred_username: Option<String>,
76        pub picture: Option<String>,
77        pub locale: Option<String>,
78    }
79
80    impl OpenIdProfile for OpenIdDataTest {
81        fn email(&self) -> Option<&str> {
82            self.email.as_deref()
83        }
84        fn name(&self) -> Option<&str> {
85            self.name.as_deref()
86        }
87        fn given_name(&self) -> Option<&str> {
88            self.given_name.as_deref()
89        }
90        fn family_name(&self) -> Option<&str> {
91            self.family_name.as_deref()
92        }
93        fn preferred_username(&self) -> Option<&str> {
94            self.preferred_username.as_deref()
95        }
96        fn picture(&self) -> Option<&str> {
97            self.picture.as_deref()
98        }
99        fn locale(&self) -> Option<&str> {
100            self.locale.as_deref()
101        }
102    }
103
104    #[test]
105    fn test_google_valid_data() {
106        let data = OpenIdDataTest {
107            email: Some("user@example.com".to_string()),
108            name: Some("Ada Lovelace".to_string()),
109            given_name: Some("Ada".to_string()),
110            family_name: Some("Lovelace".to_string()),
111            preferred_username: None,
112            picture: Some("https://example.com/avatar.png".to_string()),
113            locale: Some("en".to_string()),
114        };
115
116        assert!(data.validate().is_ok());
117    }
118
119    #[test]
120    fn test_google_invalid_email_length() {
121        let long_email = "a".repeat(EMAIL_MAX_LENGTH + 1);
122        let data = OpenIdDataTest {
123            email: Some(long_email),
124            name: None,
125            given_name: None,
126            family_name: None,
127            preferred_username: None,
128            picture: None,
129            locale: None,
130        };
131        assert!(data.validate().is_err());
132    }
133
134    #[test]
135    fn test_google_invalid_picture_url() {
136        let data = OpenIdDataTest {
137            email: None,
138            name: None,
139            given_name: None,
140            family_name: None,
141            preferred_username: None,
142            picture: Some("not-a-valid-url".to_string()),
143            locale: None,
144        };
145        assert!(data.validate().is_err());
146    }
147
148    #[test]
149    fn test_google_invalid_picture_scheme() {
150        let data = OpenIdDataTest {
151            email: None,
152            name: None,
153            given_name: None,
154            family_name: None,
155            preferred_username: None,
156            picture: Some("http://example.com/avatar.png".to_string()),
157            locale: None,
158        };
159        assert!(data.validate().is_err());
160    }
161
162    #[test]
163    fn test_google_invalid_name_length() {
164        let data = OpenIdDataTest {
165            email: None,
166            name: Some("a".repeat(NAME_MAX_LENGTH + 1)),
167            given_name: None,
168            family_name: None,
169            preferred_username: None,
170            picture: None,
171            locale: None,
172        };
173        assert!(data.validate().is_err());
174    }
175
176    #[test]
177    fn test_google_invalid_given_name_length() {
178        let data = OpenIdDataTest {
179            email: None,
180            name: None,
181            given_name: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
182            family_name: None,
183            preferred_username: None,
184            picture: None,
185            locale: None,
186        };
187        assert!(data.validate().is_err());
188    }
189
190    #[test]
191    fn test_google_invalid_family_name_length() {
192        let data = OpenIdDataTest {
193            email: None,
194            name: None,
195            given_name: None,
196            family_name: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
197            preferred_username: None,
198            picture: None,
199            locale: None,
200        };
201        assert!(data.validate().is_err());
202    }
203
204    #[test]
205    fn test_google_invalid_locale_length() {
206        let data = OpenIdDataTest {
207            email: None,
208            name: None,
209            given_name: None,
210            family_name: None,
211            preferred_username: None,
212            picture: None,
213            locale: Some("a".repeat(LOCALE_MAX_LENGTH + 1)),
214        };
215        assert!(data.validate().is_err());
216    }
217
218    #[test]
219    fn test_github_invalid_preferred_username_length() {
220        let data = OpenIdDataTest {
221            email: None,
222            name: None,
223            given_name: None,
224            family_name: None,
225            preferred_username: Some("a".repeat(SHORT_NAME_MAX_LENGTH + 1)),
226            picture: None,
227            locale: None,
228        };
229        assert!(data.validate().is_err());
230    }
231
232    #[test]
233    fn test_github_valid_data() {
234        let data = OpenIdDataTest {
235            email: Some("user@example.com".to_string()),
236            name: Some("Peter Peter Parker".to_string()),
237            given_name: None,
238            family_name: None,
239            preferred_username: Some("peterpeterparker".to_string()),
240            picture: Some("https://avatars.githubusercontent.com/u/16886711".to_string()),
241            locale: None,
242        };
243
244        assert!(data.validate().is_ok());
245    }
246}