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
9pub 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 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 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 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 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 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 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 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 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 _ = 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 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 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 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 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 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
281pub 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
289pub fn default_builder() -> UserServiceBuilder<User> {
294 builder()
295 .set_credential_validator(CredentialValidator::default())
296 .set_jwt_algorithm(Algorithm::HS256)
297}
298
299pub 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 pub fn set_credential_validator(mut self, validator: CredentialValidator) -> Self {
313 self.cred_validator = Some(validator);
314
315 self
316 }
317
318 pub fn set_jwt_algorithm(mut self, algorithm: Algorithm) -> Self {
322 self.jwt_algorithm = Some(algorithm);
323
324 self
325 }
326
327 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 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 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
371pub struct CredentialValidator {
373 pub validate_username: fn(&str) -> Result<(), AuthError>,
375 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
420pub trait AuthUser {
422 fn new(username: String, pwd_hash: String) -> Self;
424
425 fn existing(id: i32, username: String, pwd_hash: String, blocked: bool, created_at: DateTime<Utc>, updated_at: DateTime<Utc>) -> Self;
427
428 fn id(&self) -> i32;
430 fn username(&self) -> &str;
431 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 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
443pub type RoleFilter<'a> = Vec<&'a str>;
445
446#[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 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 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 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 pub fn set_name(&mut self, value: String) {
486 self.name = value;
487 self.updated_at = Utc::now();
488 }
489}
490
491#[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 let user_service = build_user_service(false, "".to_string());
684
685 let res = user_service.create_user(AVAILABLE_USERNAME.to_string(), "1qaz@WSX3edc".to_string()).await;
687
688 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 let user_service = build_user_service(false, "".to_string());
697
698 let res = user_service.create_user(EXISTING_USERNAME.to_string(), "1qaz@WSX3edc".to_string()).await;
700
701 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 let user_service = build_user_service(false, "".to_string());
710
711 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!(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 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 let res = user_service.get_authenticated_user(&token_pair.access, Some(vec!["some_role", "admin"])).await;
737
738 assert!(res.is_ok());
740 }
741
742 #[tokio::test]
743 async fn get_authenticated_user_0_not_in_role_0_returns_error() {
744 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 let res = user_service.get_authenticated_user(&token_pair.access, Some(vec!["admin"])).await;
751
752 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 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 let res = user_service.update_own_password(&token_pair.access, "123", "1qaz@WSX3edc".to_string()).await;
769
770 assert!(res.is_ok());
772 }
773
774 #[tokio::test]
775 async fn update_own_password_0_invalid_password_0_returns_error() {
776 let user_service = build_user_service(false, "".to_string());
778 let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
779
780 let res = user_service.update_own_password(&token_pair.access, "321", "1qaz@WSX3edc".to_string()).await;
782
783 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 let user_service = build_user_service(true, "".to_string());
795 let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
796
797 let res = user_service.update_own_password(&token_pair.access, "123", "1qaz@WSX3edc".to_string()).await;
799
800 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 let user_service = build_user_service(false, "".to_string());
812 let token_pair = user_service.generate_token_pair(0, &vec![]).unwrap();
813
814 let res = user_service.update_own_password(&token_pair.access, "123", "321".to_string()).await;
816
817 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 let user_service = build_user_service(false, "".to_string());
829 let user = get_existing_user(false);
830
831 let res = user_service.update_other_user_password(user.id(), "1qaz".to_string()).await;
833
834 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 let user_service = build_user_service(true, "".to_string());
842 let user = get_existing_user(false);
843
844 let res = user_service.update_other_user_password(user.id(), "1qaz".to_string()).await;
846
847 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 let user_service = build_user_service(false, "".to_string());
859
860 let res = user_service.block_user(EXISTING_USERNAME).await;
862
863 assert!(res.is_ok());
865 }
866
867 #[tokio::test]
868 async fn block_user_0_non_existent_user_0_returns_not_found_error() {
869 let user_service = build_user_service(false, "".to_string());
871
872 let res = user_service.block_user("somename").await;
874
875 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 let user_service = build_user_service(false, "".to_string());
887 let user = get_existing_user(false);
888
889 let res = user_service.generate_tokens(&user.username, "123").await;
891
892 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 let user_service = build_user_service(false, "".to_string());
909 let user = get_existing_user(false);
910
911 let res = user_service.generate_tokens(&user.username, "321").await;
913
914 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 let user_service = build_user_service(true, "".to_string());
926 let user = get_existing_user(true);
927
928 let res = user_service.generate_tokens(&user.username, "123").await;
930
931 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 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 let res = user_service.refresh_tokens(&refresh_token).await;
948
949 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 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 let res = user_service.refresh_tokens(&refresh_token_non_existent_user).await;
966
967 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 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 let res = user_service.refresh_tokens(&refresh_token).await;
984
985 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 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 let res = user_service.refresh_tokens(&refresh_token).await;
1004
1005 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}