ForgeFlux/src/auth/application/services/request_authorization/service.rs

115 lines
3.7 KiB
Rust

use std::sync::Arc;
use url::Url;
use crate::auth::application::port::out::db::save_oauth_state::SaveOAuthState;
use crate::auth::application::port::out::forge::oauth_auth_req_uri::OAuthAuthReqUri;
use crate::{
auth::application::port::out::db::errors::OutDBPortError,
utils::random_string::GenerateRandomStringInterface,
};
use super::{errors::*, RequestAuthorizationUserCase};
const STATE_LEN: usize = 8;
#[derive(Clone)]
pub struct RequestAuthorizationService {
save_oauth_state_adapter: Arc<dyn SaveOAuthState>,
oauth_auth_req_uri_adapter: Arc<dyn OAuthAuthReqUri>,
process_authorization_response_redirect_uri: Url,
generate_random_string: Arc<dyn GenerateRandomStringInterface>,
}
impl RequestAuthorizationService {
pub fn new(
save_oauth_state_adapter: Arc<dyn SaveOAuthState>,
oauth_auth_req_uri_adapter: Arc<dyn OAuthAuthReqUri>,
process_authorization_response_redirect_uri: Url,
generate_random_string: Arc<dyn GenerateRandomStringInterface>,
) -> Self {
Self {
save_oauth_state_adapter,
oauth_auth_req_uri_adapter,
process_authorization_response_redirect_uri,
generate_random_string,
}
}
}
#[async_trait::async_trait]
impl RequestAuthorizationUserCase for RequestAuthorizationService {
async fn request_authorization(
&self,
cmd: super::command::RequestAuthorizationCommand,
) -> RequestAuthorizationServiceResult<Url> {
let mut state = self.generate_random_string.get_random(STATE_LEN);
loop {
match self
.save_oauth_state_adapter
.save_oauth_state(
&state,
cmd.oauth_provider(),
&self.process_authorization_response_redirect_uri,
)
.await
{
Err(OutDBPortError::DuplicateState) => {
state = self.generate_random_string.get_random(STATE_LEN);
continue;
}
Ok(_) => break,
Err(e) => return Err(e)?,
}
}
let redirect = self
.oauth_auth_req_uri_adapter
.oauth_auth_req_uri(&state, &self.process_authorization_response_redirect_uri)?;
Ok(redirect)
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::utils::random_string::tests::*;
use crate::{
auth::application::{
port::out::{db::save_oauth_state::tests::*, forge::oauth_auth_req_uri::tests::*},
services::request_authorization::command::RequestAuthorizationCommand,
},
tests::bdd::IS_CALLED_ONLY_ONCE,
};
use super::*;
#[actix_rt::test]
async fn test_service() {
let random_string = "foorand";
let url = Url::parse("http://test_service_request_auth").unwrap();
let oauth_provider = "test_service_request_auth_oauth_provider";
let mut redirect_uri = url.clone();
redirect_uri.set_query(Some(&format!("state={random_string}")));
let mock_random_generate_string =
mock_generate_random_string(IS_CALLED_ONLY_ONCE, random_string.into());
let mock_oauth_req_uri = mock_oauth_auth_req_uri(IS_CALLED_ONLY_ONCE, redirect_uri.clone());
let mock_save_oauth_state = mock_save_oauth_state(IS_CALLED_ONLY_ONCE);
let s = RequestAuthorizationService::new(
mock_save_oauth_state,
mock_oauth_req_uri,
url.clone(),
mock_random_generate_string,
);
let cmd = RequestAuthorizationCommand::new_command(oauth_provider.to_owned()).unwrap();
let res = s.request_authorization(cmd).await.unwrap();
assert_eq!(res, redirect_uri);
}
}