some_auth/
user_service.rs

1use std::{fmt, sync::Arc};
2
3use chrono::{DateTime, TimeDelta, Utc};
4use jsonwebtoken::Algorithm;
5use regex::Regex;
6
7use crate::{error::AuthError, hasher, jwt::{self, JwtTokenSettings, TokenPair}, repository::AuthRepository};
8
9/// Provides access logic for specified [`AuthUser`]
10pub struct UserService<TAuthUser: AuthUser + fmt::Debug + Send + Sync> {
11    jwt_algorithm: Algorithm,
12    jwt_token_settings: JwtTokenSettings,
13    cred_validator: CredentialValidator,
14    repository: Arc<dyn AuthRepository<TAuthUser> + Sync + Send>
15}
16
17impl<TAuthUser: AuthUser + fmt::Debug + Send + Sync> UserService<TAuthUser> {
18    /// Creates new user and returns created id
19    pub async fn create_user(&self, username: String, password: String) -> Result<i32, AuthError> {
20        if let Some(_) = self.repository.get_user_by_username(&username).await.map_err(|err| AuthError::AuthRepositoryError(err))? {
21            return Err(AuthError::UsernameUnavailable)
22        }
23
24        (self.cred_validator.validate_username)(&username)?;
25        (self.cred_validator.validate_password)(&password)?;
26
27        let pwd_hash = hasher::bcrypt_hash(&password)?;
28
29        let user = TAuthUser::new(username, pwd_hash);
30
31        self.repository.add_user(&user).await.map_err(|err| AuthError::AuthRepositoryError(err))
32    }
33
34    /// Gets all existing users
35    pub async fn get_users(&self) -> Result<Vec<TAuthUser>, AuthError> {
36        Ok(self.repository.get_users().await.map_err(|err| AuthError::AuthRepositoryError(err))?)
37    }
38
39    /// Updates password for user with provided `access_token`
40    pub async fn update_own_password(&self, access_token: &str, old_password: &str, new_password: String) -> Result<(), AuthError> {
41        let mut user = self.get_authenticated_user(access_token, None).await?;
42
43        let check_old_pwd_res = hasher::bcrypt_verify(old_password, user.pwd_hash())?;
44        if !check_old_pwd_res {
45            return Err(AuthError::InvalidCredentials);
46        }
47
48        (self.cred_validator.validate_password)(&new_password)?;
49
50        let new_pwd_hash = hasher::bcrypt_hash(&new_password)?;
51
52        user.set_pwd_hash(new_pwd_hash);
53        user.set_updated_at(Utc::now());
54
55        _ = self.repository.update_user_refresh_token(user.id(), "",Utc::now()).await;
56        
57        self.repository.update_user(&user).await.map_err(|err| AuthError::AuthRepositoryError(err))
58    }
59
60    /// Updates other user's password
61    /// 
62    /// Note that this method doesn't use [`CredentialValidator`] for a new password validation to reset password to some default value for example
63    /// Also, this method is not recommended for self-update, use [`update_own_password`] instead
64    pub async fn update_other_user_password(&self, target_user_id: i32, target_user_new_password: String) -> Result<(), AuthError> {
65        let mut target_user = self.repository.get_user(target_user_id)
66            .await
67            .map_err(|err| AuthError::AuthRepositoryError(err))?
68            .ok_or(AuthError::UserNotFound(format!("{target_user_id}")))?;
69
70        if target_user.blocked() {
71            return Err(AuthError::InvalidOperation("user is blocked".to_string()));
72        }
73
74        let new_target_user_pwd_hash = hasher::bcrypt_hash(&target_user_new_password)?;
75
76        target_user.set_pwd_hash(new_target_user_pwd_hash);
77        target_user.set_updated_at(Utc::now());
78
79        _ = self.repository.update_user_refresh_token(target_user_id, "",Utc::now()).await;
80        
81        self.repository.update_user(&target_user).await.map_err(|err| AuthError::AuthRepositoryError(err))
82    }
83
84    /// Blocks user with provided username
85    pub async fn block_user(&self, username: &str) -> Result<(), AuthError> {
86        let mut user = self.repository.get_user_by_username(username).await
87            .map_err(|err| AuthError::AuthRepositoryError(err))?
88            .ok_or(AuthError::UserNotFound(username.to_string()))?;
89
90        user.set_blocked(true);
91        user.set_updated_at(Utc::now());
92
93        self.repository.update_user(&user).await.map_err(|err| AuthError::AuthRepositoryError(err))?;
94
95        self.repository.update_user_refresh_token(user.id(), "",Utc::now()).await
96            .map_err(|err| AuthError::AuthRepositoryError(format!("user {username} was blocked, but refresh token wasn't cleared in repository: {err}")))
97    }
98
99    /// Generates [`TokenPair`] (refresh and access tokens) by credentials
100    pub async fn generate_tokens(&self, username: &str, password: &str) -> Result<TokenPair, AuthError> {
101        let user = self.repository.get_user_by_username(username).await
102            .map_err(|err| AuthError::AuthRepositoryError(err))?
103            .ok_or(AuthError::InvalidCredentials)?;
104
105        if user.blocked() {
106            return Err(AuthError::InvalidCredentials);
107        }
108
109        let check_pwd_res = hasher::bcrypt_verify(password, user.pwd_hash())?;
110        if !check_pwd_res {
111            return Err(AuthError::InvalidCredentials);
112        }
113
114        let user_roles = self.repository.get_user_roles(user.id()).await
115            .map_err(|err| AuthError::AuthRepositoryError(err))?
116            .iter()
117            .map(|r| r.name.clone())
118            .collect();
119
120        let token_pair = self.generate_token_pair(user.id(), &user_roles)?;
121
122        self.update_hashed_refresh_in_repo(user.id(), &token_pair.refresh).await?;
123
124        Ok(token_pair)
125    }
126
127    /// Refreshes [`TokenPair`] by refresh token
128    pub async fn refresh_tokens(&self, refresh_token: &str) -> Result<TokenPair, AuthError> {
129        let decoded_token = jwt::decode_token(
130            refresh_token,
131            self.jwt_algorithm,
132            self.jwt_token_settings.refresh_tokens_secret.as_bytes())?;
133
134        let user_id: i32 = decoded_token.claims.sub.parse().map_err(|_| AuthError::InvalidCredentials)?;
135        let user = self.repository.get_user(user_id).await
136            .map_err(|err| AuthError::AuthRepositoryError(err))?
137            .ok_or(AuthError::InvalidCredentials)?;
138
139        if user.blocked() {
140            return Err(AuthError::InvalidCredentials);
141        }
142
143        // Ensure that token is actual
144        let stored_token_hash = self.repository.get_user_refresh_token(user_id).await
145            .map_err(|err| AuthError::AuthRepositoryError(err))?
146            .ok_or(AuthError::InvalidCredentials)?;
147        if !hasher::sha256_verify(refresh_token, &stored_token_hash) {
148            // if something's wrong, revoke old refresh token from repository too
149            _ = self.repository.update_user_refresh_token(user_id, "",Utc::now()).await;
150
151            return Err(AuthError::InvalidCredentials);
152        }
153
154        let token_pair = self.generate_token_pair(user_id, &decoded_token.claims.roles)?;
155
156        self.update_hashed_refresh_in_repo(user_id, &token_pair.refresh).await?;
157
158        Ok(token_pair)
159    }
160
161    /// Creates new role
162    pub async fn create_role(&self, role_name: String) -> Result<i32, AuthError> {
163        if let Some(_) = self.repository.get_role_by_name(&role_name).await.map_err(|err| AuthError::AuthRepositoryError(err))? {
164            return Err(AuthError::RoleAlreadyExists)
165        }
166
167        let role = Role::new(role_name);
168
169        self.repository.add_role(&role).await.map_err(|err| AuthError::AuthRepositoryError(err))
170    }
171
172    /// Updates role name
173    pub async fn update_role_name(&self, role_id: i32, new_name: String) -> Result<(), AuthError> {
174        if let Some(_) = self.repository.get_role_by_name(&new_name).await.map_err(|err| AuthError::AuthRepositoryError(err))? {
175            return Err(AuthError::RoleAlreadyExists)
176        }
177
178        let mut updating_role = self.repository.get_role(role_id).await
179            .map_err(|err| AuthError::AuthRepositoryError(err))?
180            .ok_or(AuthError::RoleNotFound(role_id.to_string()))?;
181
182        updating_role.set_name(new_name);
183
184        self.repository.update_role(&updating_role).await.map_err(|err| AuthError::AuthRepositoryError(err))?;
185
186        Ok(())
187    }
188
189    /// Gets all existing roles
190    pub async fn get_roles(&self) -> Result<Vec<Role>, AuthError> {
191        self.repository.get_roles().await.map_err(|err| AuthError::AuthRepositoryError(err))
192    }
193
194    /// Updates user's roles with new set of roles and clears all existing user's roles if they are not present in `roles` param.
195    pub async fn update_user_roles(&self, user_id: i32, roles: &Vec<i32>) -> Result<(), AuthError> {
196        let user = self.repository.get_user(user_id)
197            .await
198            .map_err(|err| AuthError::AuthRepositoryError(err))?
199            .ok_or(AuthError::UserNotFound(format!("{user_id}")))?;
200
201        if user.blocked() {
202            return Err(AuthError::InvalidOperation("user is blocked".to_string()));
203        }
204
205        Ok(self.repository.update_user_roles(user_id, roles).await.map_err(|err| AuthError::AuthRepositoryError(err))?)
206    }
207
208    /// Gets all user's roles
209    pub async fn get_user_roles(&self, user_id: i32) -> Result<Vec<Role>, AuthError> {
210        let _ = self.repository.get_user(user_id)
211            .await
212            .map_err(|err| AuthError::AuthRepositoryError(err))?
213            .ok_or(AuthError::UserNotFound(format!("{user_id}")))?;
214
215        self.repository.get_user_roles(user_id).await.map_err(|err| AuthError::AuthRepositoryError(err))
216    }
217
218    pub(crate) async fn get_authenticated_user<'a>(&self, access_token: &str, role_filter: Option<RoleFilter<'a>>) -> Result<TAuthUser, AuthError> {
219        let decoded_token = jwt::decode_token(
220            access_token,
221            self.jwt_algorithm,
222            self.jwt_token_settings.access_tokens_secret.as_bytes())?;
223
224        if let Some(roles_with_access) = role_filter {
225            if roles_with_access.len() == 0 {
226                return Err(AuthError::InvalidOperation("Role filter must be specified or None".to_string()))
227            }
228
229            let user_has_required_role = decoded_token.claims.roles
230                .iter()
231                .any(|ur| roles_with_access.iter().any(|rwa| rwa == ur));
232
233            if !user_has_required_role {
234                return Err(AuthError::InvalidCredentials);
235            }
236        }
237
238        let user_id: i32 = decoded_token.claims.sub.parse().map_err(|_| AuthError::InvalidCredentials)?;
239
240        let user = self.repository.get_user(user_id)
241            .await
242            .map_err(|err| AuthError::AuthRepositoryError(err))?
243            .ok_or(AuthError::InvalidCredentials)?;
244
245        if user.blocked() {
246            return Err(AuthError::InvalidCredentials);
247        }
248
249        Ok(user)
250    }
251
252    async fn update_hashed_refresh_in_repo(&self, user_id: i32, refresh_token: &str) -> Result<(), AuthError> {
253        let refresh_token_hash = hasher::sha256_hash(&refresh_token);
254        
255        Ok(self.repository.update_user_refresh_token(user_id, &refresh_token_hash,Utc::now()).await
256            .map_err(|err| AuthError::AuthRepositoryError(err))?)
257    }
258
259    fn generate_token_pair(&self, user_id: i32, roles: &Vec<String>) -> Result<TokenPair, AuthError> {
260        let refresh_token = jwt::generate_token(
261            user_id,
262            roles,
263            self.jwt_algorithm,
264            self.jwt_token_settings.refresh_tokens_lifetime,
265            self.jwt_token_settings.refresh_tokens_secret.as_bytes())?;
266
267        let access_token = jwt::generate_token(
268            user_id,
269            roles,
270            self.jwt_algorithm,
271            self.jwt_token_settings.access_tokens_lifetime,
272            self.jwt_token_settings.access_tokens_secret.as_bytes())?;
273
274        Ok(TokenPair {
275            access: access_token,
276            refresh: refresh_token
277        })
278    }
279}
280
281/// Builder to configure and build [`UserService`]
282pub struct UserServiceBuilder<TAuthUser: AuthUser + fmt::Debug + Send + Sync> {
283    jwt_algorithm: Option<Algorithm>,
284    jwt_token_settings: Option<JwtTokenSettings>,
285    cred_validator: Option<CredentialValidator>,
286    repository: Option<Arc<dyn AuthRepository<TAuthUser> + Sync + Send>>
287}
288
289/// Creates default builder with the following configuration:
290/// + Default [`User`] model
291/// + [`CredentialValidator::default`] credential validator
292/// + JWT algorithm: [`Algorithm::HS256`]
293pub fn default_builder() -> UserServiceBuilder<User> {
294    builder()
295        .set_credential_validator(CredentialValidator::default())
296        .set_jwt_algorithm(Algorithm::HS256)
297}
298
299/// Creates builder to configure and build [`UserService`].
300/// See also [`AuthUser`]
301pub fn builder<TAuthUser: AuthUser + fmt::Debug + Send + Sync>() -> UserServiceBuilder<TAuthUser> {
302    UserServiceBuilder {
303        jwt_algorithm: None,
304        jwt_token_settings: None,
305        cred_validator: None,
306        repository: None
307    }
308}
309
310impl<TAuthUser: AuthUser + fmt::Debug + Send + Sync> UserServiceBuilder<TAuthUser> {
311    /// Sets [`CredentialValidator`] which will be used to valudate [`AuthUser`] credentials in [`UserService`]
312    pub fn set_credential_validator(mut self, validator: CredentialValidator) -> Self {
313        self.cred_validator = Some(validator);
314
315        self
316    }
317
318    /// Sets jwt algorithm which will be used in [`UserService`]
319    /// 
320    /// Note that only HMAC (HS256, HS384, HS512) algorithms are supported now
321    pub fn set_jwt_algorithm(mut self, algorithm: Algorithm) -> Self {
322        self.jwt_algorithm = Some(algorithm);
323
324        self
325    }
326
327    /// Sets jwt token settings which will be used in [`UserService`]
328    /// 
329    /// Note that access and refresh token secrets are expected as raw string regardless of the chosen jwt algorithm
330    pub fn configure_jwt(mut self, jwt_token_settings: JwtTokenSettings) -> Self {
331        self.jwt_token_settings = Some(jwt_token_settings);
332
333        self
334    }
335
336    /// Sets the repository which will be used in [`UserService`]
337    pub fn use_repository(mut self, repository: Arc<dyn AuthRepository<TAuthUser> + Sync + Send>) -> Self {
338        self.repository = Some(repository);
339
340        self
341    }
342
343    /// Builds [`UserService`] 
344    /// 
345    /// Returns error, if there are some validation problems or some of the required dependencies are not configured
346    pub fn build(self) -> Result<UserService<TAuthUser>, &'static str> {        
347        let jwt_token_settings = self.jwt_token_settings.ok_or("User service jwt settings can't be empty")?;
348        
349        if jwt_token_settings.access_tokens_secret == "" || jwt_token_settings.refresh_tokens_secret == "" {
350            return Err("Access and refresh token secrets can't be empty")
351        }
352
353        if jwt_token_settings.access_tokens_lifetime <= TimeDelta::zero() || jwt_token_settings.refresh_tokens_lifetime <= TimeDelta::zero() {
354            return Err("Access and refresh token lifetimes must be positive")
355        }
356
357        let jwt_alg: Algorithm = self.jwt_algorithm.ok_or("JWT algorithm must be set")?;
358        if jwt_alg != Algorithm::HS256 && jwt_alg != Algorithm::HS384 && jwt_alg != Algorithm::HS512 {
359            return Err("Only HMAC (HS256, HS384, HS512) algorithms are supported now")
360        }
361
362        Ok(UserService {
363            jwt_algorithm: self.jwt_algorithm.ok_or("JWT algorithm must be set")?,
364            jwt_token_settings,
365            cred_validator: self.cred_validator.ok_or("Credential validator must be set")?,
366            repository: self.repository.ok_or("User service repository can't be empty")?
367        })
368    }
369}
370
371/// Credential validator which is used in [`UserService`] to validate [`AuthUser`]
372pub struct CredentialValidator {
373    /// Validates if username meets the minimum requirements
374    pub validate_username: fn(&str) -> Result<(), AuthError>,
375    /// Validates if password meets the minimum requirements
376    pub validate_password: fn(&str) -> Result<(), AuthError>
377}
378
379impl CredentialValidator {
380    fn default() -> CredentialValidator {
381        let username_validator = |username: &str| {
382            const USERNAME_REQS: &str = 
383            "username must be at least 5 characters, a combination of latin letters and numbers with one letter at least";
384
385            let length_check = username.len() >= 5;
386            let valid_chars_check = Regex::new(r"^[a-zA-Z0-9]+$").unwrap().is_match(username);
387            let contains_letter_check = Regex::new(r"[a-zA-Z]").unwrap().is_match(username);
388
389            if !(length_check && valid_chars_check && contains_letter_check) {
390                return Err(AuthError::ValidationError(USERNAME_REQS.to_string()))
391            }
392
393            Ok(())
394        };
395
396        let password_validator = |password: &str| {
397            const PWD_REQS: &str = 
398            "password must be at least 12 characters, a combination of latin uppercase and lowercase letters, numbers, and special symbols";
399
400            let length_check = password.len() >= 12;
401            let digit_check = Regex::new(r"\d").unwrap().is_match(password);
402            let uppercase_check = Regex::new(r"[A-Z]").unwrap().is_match(password);
403            let lowercase_check = Regex::new(r"[a-z]").unwrap().is_match(password);
404            let special_char_check = Regex::new(r#"[!@#$%^&*(),.?\":{}|<>]"#).unwrap().is_match(password);
405        
406            if !(length_check && digit_check && uppercase_check && lowercase_check && special_char_check) {
407                return Err(AuthError::ValidationError(PWD_REQS.to_string()))
408            }
409
410            Ok(())
411        };
412
413        CredentialValidator {
414            validate_username: username_validator,
415            validate_password: password_validator
416        }
417    }
418}
419
420/// User in auth context
421pub trait AuthUser {
422    /// Creates new user
423    fn new(username: String, pwd_hash: String) -> Self;
424
425    /// for mapping purposes
426    fn existing(id: i32, username: String, pwd_hash: String, blocked: bool, created_at: DateTime<Utc>, updated_at: DateTime<Utc>) -> Self;
427
428    // getters
429    fn id(&self) -> i32;
430    fn username(&self) -> &str;
431    /// Password hash
432    fn pwd_hash(&self) -> &str;
433    fn blocked(&self) -> bool;
434    fn created_at(&self) -> DateTime<Utc>;
435    fn updated_at(&self) -> DateTime<Utc>;
436
437    // setters
438    fn set_pwd_hash(&mut self, value: String);
439    fn set_updated_at(&mut self, value: DateTime<Utc>);
440    fn set_blocked(&mut self, value: bool);
441}
442
443/// Filter to specify for which roles api method may be accessible 
444pub type RoleFilter<'a> = Vec<&'a str>;
445
446/// User's role
447#[derive(Debug, Clone)]
448pub struct Role {
449    id: i32,
450    name: String,
451    created_at: DateTime<Utc>,
452    updated_at: DateTime<Utc>
453}
454
455impl Role {
456    /// Creates new role
457    pub fn new(name: String) -> Role {
458        let now: DateTime<Utc> = Utc::now();
459
460        Role {
461            id: 0,
462            name,
463            created_at: now,
464            updated_at: now
465        }
466    }
467
468    /// for mapping purposes only
469    pub fn existing(id: i32, name: String, created_at: DateTime<Utc>, updated_at: DateTime<Utc>) -> Role {
470        Role {
471            id,
472            name,
473            created_at,
474            updated_at
475        }
476    }
477
478    //getters
479    pub fn id(&self) -> i32 { self.id }
480    pub fn name(&self) -> &str { &self.name }
481    pub fn created_at(&self) -> DateTime<Utc> { self.created_at }
482    pub fn updated_at(&self) -> DateTime<Utc> { self.updated_at }
483
484    // setters
485    pub fn set_name(&mut self, value: String) {
486        self.name = value;
487        self.updated_at = Utc::now();
488    }
489}
490
491/// Default implementation of [`AuthUser`]
492#[derive(Clone)]
493pub struct User {
494    id: i32,
495    username: String,
496    pwd_hash: String,
497    blocked: bool,
498    created_at: DateTime<Utc>,
499    updated_at: DateTime<Utc>
500}
501
502impl fmt::Debug for User {
503    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504        f.debug_struct("User")
505            .field("id", &self.id)
506            .field("username", &self.username)
507            .field("pwd_hash", &"***")
508            .field("created_at", &self.created_at)
509            .field("updated_at", &self.updated_at)
510            .field("blocked", &self.blocked)
511            .finish()
512    }
513}
514
515impl AuthUser for User {
516    fn new(username: String, pwd_hash: String) -> Self {
517        let now: DateTime<Utc> = Utc::now();
518
519        User {
520            id: 0,
521            username,
522            pwd_hash,
523            blocked: false,
524            created_at: now,
525            updated_at: now,
526        }
527    }
528    
529    fn existing(id: i32, username: String, pwd_hash: String, blocked: bool, created_at: DateTime<Utc>, updated_at: DateTime<Utc>) -> Self {
530        User {
531            id,
532            username,
533            pwd_hash,
534            blocked,
535            created_at,
536            updated_at            
537        }
538    }
539
540    fn id(&self) -> i32 { self.id }
541    fn username(&self) -> &str { &self.username }
542    fn pwd_hash(&self) -> &str { &self.pwd_hash }
543    fn blocked(&self) -> bool { self.blocked }
544    fn created_at(&self) -> DateTime<Utc> { self.created_at }
545    fn updated_at(&self) -> DateTime<Utc> { self.updated_at }
546    
547    fn set_pwd_hash(&mut self, value: String) { self.pwd_hash = value; }    
548    fn set_updated_at(&mut self, value: DateTime<Utc>) { self.updated_at = value; }    
549    fn set_blocked(&mut self, value: bool) { self.blocked = value; }
550}
551
552#[cfg(test)]
553mod tests {    
554    use std::{thread::sleep, time::Duration};
555    
556    use mockall::predicate;
557
558    use crate::repository::MockAuthRepository;
559
560    use super::*;
561
562    #[test]
563    fn validate_username_0_with_letters_and_numbers_0_ok() {
564        let validator = CredentialValidator::default();
565        let username = "u1s2e3r";
566
567        let res = (validator.validate_username)(username);
568
569        assert!(res.is_ok())
570    }
571
572    #[test]
573    fn validate_username_0_only_letters_0_ok() {
574        let validator = CredentialValidator::default();
575        let username = "userr";
576
577        let res = (validator.validate_username)(username);
578
579        assert!(res.is_ok())
580    }
581
582    #[test]
583    fn validate_username_0_only_numbers_0_err() {
584        let validator = CredentialValidator::default();
585        let username = "12345";
586
587        let res = (validator.validate_username)(username);
588
589        assert!(res.is_err());
590        assert!(res.unwrap_err().to_string().contains("validation"))
591    }
592
593    #[test]
594    fn validate_username_0_too_short_0_err() {
595        let validator = CredentialValidator::default();
596        let username = "user";
597
598        let res = (validator.validate_username)(username);
599
600        assert!(res.is_err());
601        assert!(res.unwrap_err().to_string().contains("validation"))
602    }
603
604    #[test]
605    fn validate_username_0_non_latin_0_err() {
606        let validator = CredentialValidator::default();
607        let username = "ユーザー";
608
609        let res = (validator.validate_username)(username);
610
611        assert!(res.is_err());
612        assert!(res.unwrap_err().to_string().contains("validation"))
613    }
614
615    #[test]
616    fn validate_password_0_all_requirements_0_ok() {
617        let validator = CredentialValidator::default();
618        let password = "1qaz@WSX3edc";
619
620        let res = (validator.validate_password)(password);
621
622        assert!(res.is_ok())
623    }
624
625    #[test]
626    fn validate_password_0_no_special_simbols_0_err() {
627        let validator = CredentialValidator::default();
628        let password = "1qaz2WSX3edc";
629
630        let res = (validator.validate_password)(password);
631
632        assert!(res.is_err());
633        assert!(res.unwrap_err().to_string().contains("validation"))
634    }
635
636    #[test]
637    fn validate_password_0_no_digits_0_err() {
638        let validator = CredentialValidator::default();
639        let password = "!qaz@WSX#edc";
640
641        let res = (validator.validate_password)(password);
642
643        assert!(res.is_err());
644        assert!(res.unwrap_err().to_string().contains("validation"))
645    }
646
647    #[test]
648    fn validate_password_0_no_uppercases_0_err() {
649        let validator = CredentialValidator::default();
650        let password = "1qaz@wsx#edc";
651
652        let res = (validator.validate_password)(password);
653
654        assert!(res.is_err());
655        assert!(res.unwrap_err().to_string().contains("validation"))
656    }
657
658    #[test]
659    fn validate_password_0_no_lowercases_0_err() {
660        let validator = CredentialValidator::default();
661        let password = "1QAZ@WSX3EDC";
662
663        let res = (validator.validate_password)(password);
664
665        assert!(res.is_err());
666        assert!(res.unwrap_err().to_string().contains("validation"))
667    }
668
669    #[test]
670    fn validate_password_0_too_short_0_err() {
671        let validator = CredentialValidator::default();
672        let password = "1qaz@WSX";
673
674        let res = (validator.validate_password)(password);
675
676        assert!(res.is_err());
677        assert!(res.unwrap_err().to_string().contains("validation"))
678    }
679
680    #[tokio::test]
681    async fn create_user_test() {
682        // Arrange
683        let user_service = build_user_service(false, "".to_string());
684
685        // Act
686        let res = user_service.create_user(AVAILABLE_USERNAME.to_string(), "1qaz@WSX3edc".to_string()).await;
687
688        //Assert
689        assert!(res.is_ok());
690        assert_eq!(1, res.unwrap())
691    }
692
693    #[tokio::test]
694    async fn create_user_0_existing_usernaime_0_returns_error() {
695        // Arrange
696        let user_service = build_user_service(false, "".to_string());
697
698        // Act
699        let res = user_service.create_user(EXISTING_USERNAME.to_string(), "1qaz@WSX3edc".to_string()).await;
700
701        //Assert
702        assert!(res.is_err());
703        assert_eq!(AuthError::UsernameUnavailable.to_string(), res.unwrap_err().to_string());
704    }
705
706    #[tokio::test]
707    async fn create_user_0_non_valid_username_or_password_validation_0_returns_err() {
708        // Arrange
709        let user_service = build_user_service(false, "".to_string());
710
711        // Act
712        let bad_username = user_service.create_user("usr".to_string(), "1qaz@WSX3edc".to_string()).await;
713        let bad_pwd = user_service.create_user(AVAILABLE_USERNAME.to_string(), "1qaz".to_string()).await;
714
715        //Assert
716        assert!(bad_username.is_err());
717        assert!(bad_pwd.is_err());
718        match bad_username.unwrap_err() {
719            AuthError::ValidationError(msg) => assert!(msg.contains("username")),
720            _ => panic!("Error is not ValidationError")
721        };
722        match bad_pwd.unwrap_err() {
723            AuthError::ValidationError(msg) => assert!(msg.contains("password")),
724            _ => panic!("Error is not ValidationError")
725        };
726    }
727
728    #[tokio::test]
729    async fn get_authenticated_user_0_correct_role_0_ok() {
730        // Arrange
731        let user_service = build_user_service(false, "".to_string());
732        let user = get_existing_user(false);
733        let token_pair = user_service.generate_token_pair(user.id, &vec!["some_role".to_string(), "one_more_role".to_string()]).unwrap();
734
735        // Act
736        let res = user_service.get_authenticated_user(&token_pair.access, Some(vec!["some_role", "admin"])).await;
737
738        // Assert
739        assert!(res.is_ok());
740    }
741
742    #[tokio::test]
743    async fn get_authenticated_user_0_not_in_role_0_returns_error() {
744        // Arrange
745        let user_service = build_user_service(false, "".to_string());
746        let user = get_existing_user(false);
747        let token_pair = user_service.generate_token_pair(user.id, &vec!["some_role".to_string()]).unwrap();
748
749        // Act
750        let res = user_service.get_authenticated_user(&token_pair.access, Some(vec!["admin"])).await;
751
752        // Assert
753        assert!(res.is_err());
754        match res.unwrap_err() {
755            AuthError::InvalidCredentials => (),
756            _ => panic!("Error is not InvalidCredentials")
757        }
758    }
759
760    #[tokio::test]
761    async fn update_own_password_test() {
762        // Arrange
763        let user_service = build_user_service(false, "".to_string());
764        let user = get_existing_user(false);
765        let token_pair = user_service.generate_token_pair(user.id, &vec![]).unwrap();
766
767        // Act
768        let res = user_service.update_own_password(&token_pair.access, "123", "1qaz@WSX3edc".to_string()).await;
769
770        //Assert
771        assert!(res.is_ok());
772    }
773
774    #[tokio::test]
775    async fn update_own_password_0_invalid_password_0_returns_error() {
776        // Arrange
777        let user_service = build_user_service(false, "".to_string());
778        let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
779
780        // Act
781        let res = user_service.update_own_password(&token_pair.access, "321", "1qaz@WSX3edc".to_string()).await;
782
783        //Assert
784        assert!(res.is_err());
785        match res.unwrap_err() {
786            AuthError::InvalidCredentials => (),
787            _ => panic!("Error is not InvalidCredentials")
788        }
789    }
790
791    #[tokio::test]
792    async fn update_own_password_0_blocked_user_0_returns_error() {
793        // Arrange
794        let user_service = build_user_service(true, "".to_string());
795        let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
796
797        // Act
798        let res = user_service.update_own_password(&token_pair.access, "123", "1qaz@WSX3edc".to_string()).await;
799
800        //Assert
801        assert!(res.is_err());
802        match res.unwrap_err() {
803            AuthError::InvalidCredentials => (),
804            _ => panic!("Error is not InvalidCredentials")
805        }
806    }
807
808    #[tokio::test]
809    async fn update_own_password_0_weak_password_0_returns_error() {
810        // Arrange
811        let user_service = build_user_service(false, "".to_string());
812        let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
813
814        // Act
815        let res = user_service.update_own_password(&token_pair.access, "123", "321".to_string()).await;
816
817        //Assert
818        assert!(res.is_err());
819        match res.unwrap_err() {
820            AuthError::ValidationError(msg) => assert!(msg.contains("password")),
821            _ => panic!("Error is not ValidationError")
822        };
823    }
824
825    #[tokio::test]
826    async fn update_user_password_by_admin_test() {
827        // Arrange
828        let user_service = build_user_service(false, "".to_string());
829        let user = get_existing_user(false);
830
831        // Act
832        let res = user_service.update_other_user_password(user.id(), "1qaz".to_string()).await;
833
834        //Assert
835        assert!(res.is_ok());
836    }
837
838    #[tokio::test]
839    async fn update_user_password_by_admin_0_for_blocked_user_0_returns_invalid_operation() {
840        // Arrange
841        let user_service = build_user_service(true, "".to_string());
842        let user = get_existing_user(false);
843
844        // Act
845        let res = user_service.update_other_user_password(user.id(), "1qaz".to_string()).await;
846
847        //Assert
848        assert!(res.is_err());
849        match res.unwrap_err() {
850            AuthError::InvalidOperation(message) => assert!(message.contains("blocked")),
851            _ => panic!("Error is not InvalidOperation")
852        }
853    }
854
855    #[tokio::test]
856    async fn block_user_test() {
857        // Arrange
858        let user_service = build_user_service(false, "".to_string());
859
860        // Act
861        let res = user_service.block_user(EXISTING_USERNAME).await;
862
863        //Assert
864        assert!(res.is_ok());
865    }
866
867    #[tokio::test]
868    async fn block_user_0_non_existent_user_0_returns_not_found_error() {
869        // Arrange
870        let user_service = build_user_service(false, "".to_string());
871
872        // Act
873        let res = user_service.block_user("somename").await;
874
875        //Assert
876        assert!(res.is_err());
877        match res.unwrap_err() {
878            AuthError::UserNotFound(msg) => assert!(msg.contains("somename")),
879            _ => panic!("Error is not UserNotFound")
880        };
881    }
882
883    #[tokio::test]
884    async fn generate_tokens_test() {
885        // Arrange
886        let user_service = build_user_service(false, "".to_string());
887        let user = get_existing_user(false);
888
889        // Act
890        let res = user_service.generate_tokens(&user.username, "123").await;
891
892        //Assert
893        assert!(res.is_ok());
894        let token_pair = res.unwrap();
895        assert!(token_pair.access != "");
896        assert!(token_pair.refresh != "");
897
898        let decoded = jwt::decode_token(&token_pair.access, Algorithm::HS256, "Sup$rS4ccrettt".as_bytes());
899        assert!(decoded.is_ok());
900        let claims = decoded.unwrap().claims;
901        assert!(claims.roles.iter().any(|r| r == "admin"));
902        assert!(claims.roles.iter().any(|r| r == "adm"));
903    }
904
905    #[tokio::test]
906    async fn generate_tokens_0_invalid_password_0_returns_error() {
907        // Arrange
908        let user_service = build_user_service(false, "".to_string());
909        let user = get_existing_user(false);
910
911        // Act
912        let res = user_service.generate_tokens(&user.username, "321").await;
913
914        //Assert
915        assert!(res.is_err());
916        match res.unwrap_err() {
917            AuthError::InvalidCredentials => (),
918            _ => panic!("Error is not InvalidCredentials")
919        };
920    }
921
922    #[tokio::test]
923    async fn generate_tokens_0_blocked_user_0_returns_error() {
924        // Arrange
925        let user_service = build_user_service(true, "".to_string());
926        let user = get_existing_user(true);
927
928        // Act
929        let res = user_service.generate_tokens(&user.username, "123").await;
930
931        //Assert
932        assert!(res.is_err());
933        match res.unwrap_err() {
934            AuthError::InvalidCredentials => (),
935            _ => panic!("Error is not InvalidCredentials")
936        };
937    }
938
939    #[tokio::test]
940    async fn refresh_tokens_test() {
941        // Arrange
942        let user = get_existing_user(false);
943        let refresh_token = get_user_refresh_token(user.id);
944        let user_service = build_user_service(false, refresh_token.clone());
945
946        // Act
947        let res = user_service.refresh_tokens(&refresh_token).await;
948
949        //Assert
950        assert!(res.is_ok());
951        let token_pair = res.unwrap();
952        assert!(token_pair.access != "");
953        assert!(token_pair.refresh != "");
954    }
955
956    #[tokio::test]
957    async fn refresh_tokens_0_non_existent_user_0_returns_invalid_credentials() {
958        // Arrange
959        let user = get_existing_user(false);
960        let refresh_token = get_user_refresh_token(user.id);
961        let refresh_token_non_existent_user = get_user_refresh_token(100);
962        let user_service = build_user_service(false, refresh_token.clone());
963
964        // Act
965        let res = user_service.refresh_tokens(&refresh_token_non_existent_user).await;
966
967        //Assert
968        assert!(res.is_err());
969        match res.unwrap_err() {
970            AuthError::InvalidCredentials => (),
971            _ => panic!("Error is not InvalidCredentials")
972        };
973    }
974
975    #[tokio::test]
976    async fn refresh_tokens_0_blocked_user_0_returns_invalid_credentials() {
977        // Arrange
978        let user = get_existing_user(true);
979        let refresh_token = get_user_refresh_token(user.id);
980        let user_service = build_user_service(true, refresh_token.clone());
981
982        // Act
983        let res = user_service.refresh_tokens(&refresh_token).await;
984
985        //Assert
986        assert!(res.is_err());
987        match res.unwrap_err() {
988            AuthError::InvalidCredentials => (),
989            _ => panic!("Error is not InvalidCredentials")
990        };
991    }
992
993    #[tokio::test]
994    async fn refresh_tokens_0_unactual_token_0_returns_invalid_credentials() {
995        // Arrange
996        let user = get_existing_user(false);
997        let refresh_token = get_user_refresh_token(user.id);
998        sleep(Duration::from_secs(1));
999        let new_refresh_token = get_user_refresh_token(user.id);
1000        let user_service = build_user_service(false, new_refresh_token);
1001
1002        // Act
1003        let res = user_service.refresh_tokens(&refresh_token).await;
1004
1005        //Assert
1006        assert!(res.is_err());
1007        match res.unwrap_err() {
1008            AuthError::InvalidCredentials => (),
1009            _ => panic!("Error is not InvalidCredentials")
1010        };
1011    }
1012
1013    const EXISTING_USERNAME: &str = "existing";
1014    const AVAILABLE_USERNAME: &str = "admin";
1015    const ADMIN_ID: i32 = 42;
1016
1017    fn build_user_service(blocked_user: bool, user_refresh_token: String) -> UserService<User> {
1018        let existing_user = get_existing_user(blocked_user);
1019        let existing_user_clone = existing_user.clone();
1020        let existing_username = existing_user.username.clone();
1021        let existing_admin = get_existing_admin();
1022
1023        let builder = default_builder()
1024            .configure_jwt(JwtTokenSettings {
1025                access_tokens_lifetime: TimeDelta::minutes(5),
1026                refresh_tokens_lifetime: TimeDelta::days(7),
1027                access_tokens_secret: "Sup$rS4ccrettt".to_string(),
1028                refresh_tokens_secret: "AnotherSup$rS4ccrettt".to_string(),
1029            });
1030
1031            
1032        let mut repository_mock = MockAuthRepository::new();
1033
1034        repository_mock
1035            .expect_get_user_by_username()
1036            .with(predicate::function(move |name| name == existing_username))
1037            .returning(move |_| Ok(Some(existing_user.clone())));
1038        repository_mock
1039            .expect_get_user_by_username()
1040            .with(predicate::always())
1041            .returning(move |_| Ok(None));
1042
1043        repository_mock
1044            .expect_get_user()
1045            .with(predicate::function(move |&id| id == ADMIN_ID))
1046            .returning(move |_| Ok(Some(existing_admin.clone())));
1047        repository_mock
1048            .expect_get_user()
1049            .with(predicate::always())
1050            .returning(move |_| Ok(Some(existing_user_clone.clone())));
1051
1052        repository_mock
1053            .expect_add_user()
1054            .with(predicate::always())
1055            .returning(move |_| Ok(1));
1056
1057        repository_mock
1058            .expect_update_user()
1059            .with(predicate::always())
1060            .returning(move |_| Ok(()));
1061
1062        repository_mock
1063            .expect_update_user_refresh_token()
1064            .with(predicate::always(), predicate::always(), predicate::always())
1065            .returning(move |_, _, _| Ok(()));
1066
1067        repository_mock
1068            .expect_get_user_refresh_token()
1069            .with(predicate::always())
1070            .returning(move |_| Ok(Some(hasher::sha256_hash(&user_refresh_token))));
1071
1072        repository_mock
1073            .expect_get_user_roles()
1074            .with(predicate::always())
1075            .returning(move |_| Ok(vec![Role::existing(1, "admin".to_string(), Utc::now(), Utc::now()), Role::existing(2, "adm".to_string(), Utc::now(), Utc::now())]));
1076
1077        builder.use_repository(Arc::new(repository_mock)).build().unwrap()
1078    }
1079
1080    fn get_existing_user(blocked: bool) -> User {
1081        let now: DateTime<Utc> = Utc::now();
1082
1083        User {
1084            id: 0,
1085            username: EXISTING_USERNAME.to_string(),
1086            blocked,
1087            pwd_hash: hasher::bcrypt_hash("123").unwrap(),
1088            created_at: now,
1089            updated_at: now            
1090        }
1091    }
1092
1093    fn get_user_refresh_token(user_id: i32) -> String {
1094        jwt::generate_token(
1095            user_id,
1096            &vec![],
1097            Algorithm::HS256,
1098            TimeDelta::days(7),
1099            "AnotherSup$rS4ccrettt".as_bytes())
1100            .unwrap()
1101    }
1102
1103    fn get_existing_admin() -> User {
1104        let now = Utc::now();
1105
1106        User {
1107            id: ADMIN_ID,
1108            username: "admin".to_string(),
1109            blocked: false,
1110            pwd_hash: hasher::bcrypt_hash("456").unwrap(),
1111            created_at: now,
1112            updated_at: now            
1113        }
1114    }
1115}