use super::jwt; use crate::database::establish_connection; use crate::models::user::rss_user::User; use crate::schema::users; use actix_web::dev::ServiceRequest; use diesel::prelude::*; /// Decodes the token and confirms it hasn't been revoked, i.e. its `token_version` /// still matches the one stored on the user (bumped on logout / password change). pub fn check_token(token: String) -> Result { let decoded = jwt::JwtToken::decode(token)?; let mut connection = establish_connection(); let user: User = users::table .find(decoded.user_id) .first(&mut connection) .map_err(|_| "could not decode token")?; if user.token_version != decoded.token_version { return Err("token has been revoked"); } Ok(decoded.user_id) } pub fn extract_header_token(request: &ServiceRequest) -> Result { match request.headers().get("user-token") { Some(token) => match token.to_str() { Ok(processed_token) => Ok(String::from(processed_token)), Err(_) => Err("there was an error processing token"), }, None => Err("there is no token"), } } #[cfg(test)] mod processes_test { use actix_web::test::TestRequest; use diesel::prelude::*; use crate::auth::jwt::JwtToken; use crate::database::establish_connection; use crate::test_helpers::{delete_user, insert_user}; use super::check_token; #[test] fn check_correct_token() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); let token: String = JwtToken::encode(user.id, user.token_version); let result = check_token(token); assert_eq!(Ok(user.id), result); delete_user(&mut connection, user.id); } #[test] fn revoked_token_is_rejected() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); // Token signed with the user's current version, then the version is bumped // (as logout would do), which must invalidate the previously issued token. let token: String = JwtToken::encode(user.id, user.token_version); diesel::update(crate::schema::users::table.find(user.id)) .set(crate::schema::users::token_version.eq(user.token_version + 1)) .execute(&mut connection) .unwrap(); assert_eq!(Err("token has been revoked"), check_token(token)); delete_user(&mut connection, user.id); } #[test] fn incorrect_check_token() { let token: String = String::from("test"); assert_eq!(Err("could not decode token"), check_token(token)); } #[test] fn successful_extract_header_token() { let request = TestRequest::default() .insert_header(("user-token", "token")) .to_srv_request(); match super::extract_header_token(&request) { Ok(processed_password) => assert_eq!("token", processed_password), _ => panic!("failed extract_header_token"), } } #[test] fn failed_extract_header_token() { let request = TestRequest::default() .insert_header(("wrong", "bla")) .to_srv_request(); match super::extract_header_token(&request) { Err(processed_password) => assert_eq!("there is no token", processed_password), _ => panic!("Extract header token should fail when not provided."), } } }